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

1099 lines
33 KiB
C++

//===- lib/ReaderWriter/MachO/MachOLinkingContext.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 "lld/Common/ErrorHandler.h"
#include "lld/ReaderWriter/MachOLinkingContext.h"
#include "ArchHandler.h"
#include "File.h"
#include "FlatNamespaceFile.h"
#include "MachONormalizedFile.h"
#include "MachOPasses.h"
#include "SectCreateFile.h"
#include "lld/Common/Driver.h"
#include "lld/Core/ArchiveLibraryFile.h"
#include "lld/Core/PassManager.h"
#include "lld/Core/Reader.h"
#include "lld/Core/Writer.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include <algorithm>
using lld::mach_o::ArchHandler;
using lld::mach_o::MachOFile;
using lld::mach_o::MachODylibFile;
using namespace llvm::MachO;
namespace lld {
bool MachOLinkingContext::parsePackedVersion(StringRef str, uint32_t &result) {
result = 0;
if (str.empty())
return false;
SmallVector<StringRef, 3> parts;
llvm::SplitString(str, parts, ".");
unsigned long long num;
if (llvm::getAsUnsignedInteger(parts[0], 10, num))
return true;
if (num > 65535)
return true;
result = num << 16;
if (parts.size() > 1) {
if (llvm::getAsUnsignedInteger(parts[1], 10, num))
return true;
if (num > 255)
return true;
result |= (num << 8);
}
if (parts.size() > 2) {
if (llvm::getAsUnsignedInteger(parts[2], 10, num))
return true;
if (num > 255)
return true;
result |= num;
}
return false;
}
bool MachOLinkingContext::parsePackedVersion(StringRef str, uint64_t &result) {
result = 0;
if (str.empty())
return false;
SmallVector<StringRef, 5> parts;
llvm::SplitString(str, parts, ".");
unsigned long long num;
if (llvm::getAsUnsignedInteger(parts[0], 10, num))
return true;
if (num > 0xFFFFFF)
return true;
result = num << 40;
unsigned Shift = 30;
for (StringRef str : llvm::makeArrayRef(parts).slice(1)) {
if (llvm::getAsUnsignedInteger(str, 10, num))
return true;
if (num > 0x3FF)
return true;
result |= (num << Shift);
Shift -= 10;
}
return false;
}
MachOLinkingContext::ArchInfo MachOLinkingContext::_s_archInfos[] = {
{ "x86_64", arch_x86_64, true, CPU_TYPE_X86_64, CPU_SUBTYPE_X86_64_ALL },
{ "i386", arch_x86, true, CPU_TYPE_I386, CPU_SUBTYPE_X86_ALL },
{ "ppc", arch_ppc, false, CPU_TYPE_POWERPC, CPU_SUBTYPE_POWERPC_ALL },
{ "armv6", arch_armv6, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V6 },
{ "armv7", arch_armv7, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7 },
{ "armv7s", arch_armv7s, true, CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S },
{ "arm64", arch_arm64, true, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL },
{ "", arch_unknown,false, 0, 0 }
};
MachOLinkingContext::Arch
MachOLinkingContext::archFromCpuType(uint32_t cputype, uint32_t cpusubtype) {
for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
if ((info->cputype == cputype) && (info->cpusubtype == cpusubtype))
return info->arch;
}
return arch_unknown;
}
MachOLinkingContext::Arch
MachOLinkingContext::archFromName(StringRef archName) {
for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
if (info->archName.equals(archName))
return info->arch;
}
return arch_unknown;
}
StringRef MachOLinkingContext::nameFromArch(Arch arch) {
for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
if (info->arch == arch)
return info->archName;
}
return "<unknown>";
}
uint32_t MachOLinkingContext::cpuTypeFromArch(Arch arch) {
assert(arch != arch_unknown);
for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
if (info->arch == arch)
return info->cputype;
}
llvm_unreachable("Unknown arch type");
}
uint32_t MachOLinkingContext::cpuSubtypeFromArch(Arch arch) {
assert(arch != arch_unknown);
for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
if (info->arch == arch)
return info->cpusubtype;
}
llvm_unreachable("Unknown arch type");
}
bool MachOLinkingContext::isThinObjectFile(StringRef path, Arch &arch) {
return mach_o::normalized::isThinObjectFile(path, arch);
}
bool MachOLinkingContext::sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset,
uint32_t &size) {
return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size);
}
MachOLinkingContext::MachOLinkingContext() {}
MachOLinkingContext::~MachOLinkingContext() {
// Atoms are allocated on BumpPtrAllocator's on File's.
// As we transfer atoms from one file to another, we need to clear all of the
// atoms before we remove any of the BumpPtrAllocator's.
auto &nodes = getNodes();
for (unsigned i = 0, e = nodes.size(); i != e; ++i) {
FileNode *node = dyn_cast<FileNode>(nodes[i].get());
if (!node)
continue;
File *file = node->getFile();
file->clearAtoms();
}
}
void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os,
uint32_t minOSVersion,
bool exportDynamicSymbols) {
_outputMachOType = type;
_arch = arch;
_os = os;
_osMinVersion = minOSVersion;
// If min OS not specified on command line, use reasonable defaults.
// Note that we only do sensible defaults when emitting something other than
// object and preload.
if (_outputMachOType != llvm::MachO::MH_OBJECT &&
_outputMachOType != llvm::MachO::MH_PRELOAD) {
if (minOSVersion == 0) {
switch (_arch) {
case arch_x86_64:
case arch_x86:
parsePackedVersion("10.8", _osMinVersion);
_os = MachOLinkingContext::OS::macOSX;
break;
case arch_armv6:
case arch_armv7:
case arch_armv7s:
case arch_arm64:
parsePackedVersion("7.0", _osMinVersion);
_os = MachOLinkingContext::OS::iOS;
break;
default:
break;
}
}
}
switch (_outputMachOType) {
case llvm::MachO::MH_EXECUTE:
// If targeting newer OS, use _main
if (minOS("10.8", "6.0")) {
_entrySymbolName = "_main";
} else {
// If targeting older OS, use start (in crt1.o)
_entrySymbolName = "start";
}
// __PAGEZERO defaults to 4GB on 64-bit (except for PP64 which lld does not
// support) and 4KB on 32-bit.
if (is64Bit(_arch)) {
_pageZeroSize = 0x100000000;
} else {
_pageZeroSize = 0x1000;
}
// Initial base address is __PAGEZERO size.
_baseAddress = _pageZeroSize;
// Make PIE by default when targetting newer OSs.
switch (os) {
case OS::macOSX:
if (minOSVersion >= 0x000A0700) // MacOSX 10.7
_pie = true;
break;
case OS::iOS:
if (minOSVersion >= 0x00040300) // iOS 4.3
_pie = true;
break;
case OS::iOS_simulator:
_pie = true;
break;
case OS::unknown:
break;
}
setGlobalsAreDeadStripRoots(exportDynamicSymbols);
break;
case llvm::MachO::MH_DYLIB:
setGlobalsAreDeadStripRoots(exportDynamicSymbols);
break;
case llvm::MachO::MH_BUNDLE:
break;
case llvm::MachO::MH_OBJECT:
_printRemainingUndefines = false;
_allowRemainingUndefines = true;
break;
default:
break;
}
// Set default segment page sizes based on arch.
if (arch == arch_arm64)
_pageSize = 4*4096;
}
uint32_t MachOLinkingContext::getCPUType() const {
return cpuTypeFromArch(_arch);
}
uint32_t MachOLinkingContext::getCPUSubType() const {
return cpuSubtypeFromArch(_arch);
}
bool MachOLinkingContext::is64Bit(Arch arch) {
for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
if (info->arch == arch) {
return (info->cputype & CPU_ARCH_ABI64);
}
}
// unknown archs are not 64-bit.
return false;
}
bool MachOLinkingContext::isHostEndian(Arch arch) {
assert(arch != arch_unknown);
for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
if (info->arch == arch) {
return (info->littleEndian == llvm::sys::IsLittleEndianHost);
}
}
llvm_unreachable("Unknown arch type");
}
bool MachOLinkingContext::isBigEndian(Arch arch) {
assert(arch != arch_unknown);
for (ArchInfo *info = _s_archInfos; !info->archName.empty(); ++info) {
if (info->arch == arch) {
return ! info->littleEndian;
}
}
llvm_unreachable("Unknown arch type");
}
bool MachOLinkingContext::is64Bit() const {
return is64Bit(_arch);
}
bool MachOLinkingContext::outputTypeHasEntry() const {
switch (_outputMachOType) {
case MH_EXECUTE:
case MH_DYLINKER:
case MH_PRELOAD:
return true;
default:
return false;
}
}
bool MachOLinkingContext::needsStubsPass() const {
switch (_outputMachOType) {
case MH_EXECUTE:
return !_outputMachOTypeStatic;
case MH_DYLIB:
case MH_BUNDLE:
return true;
default:
return false;
}
}
bool MachOLinkingContext::needsGOTPass() const {
// GOT pass not used in -r mode.
if (_outputMachOType == MH_OBJECT)
return false;
// Only some arches use GOT pass.
switch (_arch) {
case arch_x86_64:
case arch_arm64:
return true;
default:
return false;
}
}
bool MachOLinkingContext::needsCompactUnwindPass() const {
switch (_outputMachOType) {
case MH_EXECUTE:
case MH_DYLIB:
case MH_BUNDLE:
return archHandler().needsCompactUnwind();
default:
return false;
}
}
bool MachOLinkingContext::needsObjCPass() const {
// ObjC pass is only needed if any of the inputs were ObjC.
return _objcConstraint != objc_unknown;
}
bool MachOLinkingContext::needsShimPass() const {
// Shim pass only used in final executables.
if (_outputMachOType == MH_OBJECT)
return false;
// Only 32-bit arm arches use Shim pass.
switch (_arch) {
case arch_armv6:
case arch_armv7:
case arch_armv7s:
return true;
default:
return false;
}
}
bool MachOLinkingContext::needsTLVPass() const {
switch (_outputMachOType) {
case MH_BUNDLE:
case MH_EXECUTE:
case MH_DYLIB:
return true;
default:
return false;
}
}
StringRef MachOLinkingContext::binderSymbolName() const {
return archHandler().stubInfo().binderSymbolName;
}
bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const {
uint32_t parsedVersion;
switch (_os) {
case OS::macOSX:
if (parsePackedVersion(mac, parsedVersion))
return false;
return _osMinVersion >= parsedVersion;
case OS::iOS:
case OS::iOS_simulator:
if (parsePackedVersion(iOS, parsedVersion))
return false;
return _osMinVersion >= parsedVersion;
case OS::unknown:
// If we don't know the target, then assume that we don't meet the min OS.
// This matches the ld64 behaviour
return false;
}
llvm_unreachable("invalid OS enum");
}
bool MachOLinkingContext::addEntryPointLoadCommand() const {
if ((_outputMachOType == MH_EXECUTE) && !_outputMachOTypeStatic) {
return minOS("10.8", "6.0");
}
return false;
}
bool MachOLinkingContext::addUnixThreadLoadCommand() const {
switch (_outputMachOType) {
case MH_EXECUTE:
if (_outputMachOTypeStatic)
return true;
else
return !minOS("10.8", "6.0");
break;
case MH_DYLINKER:
case MH_PRELOAD:
return true;
default:
return false;
}
}
bool MachOLinkingContext::pathExists(StringRef path) const {
if (!_testingFileUsage)
return llvm::sys::fs::exists(path.str());
// Otherwise, we're in test mode: only files explicitly provided on the
// command-line exist.
std::string key = path.str();
std::replace(key.begin(), key.end(), '\\', '/');
return _existingPaths.find(key) != _existingPaths.end();
}
bool MachOLinkingContext::fileExists(StringRef path) const {
bool found = pathExists(path);
// Log search misses.
if (!found)
addInputFileNotFound(path);
// When testing, file is never opened, so logging is done here.
if (_testingFileUsage && found)
addInputFileDependency(path);
return found;
}
void MachOLinkingContext::setSysLibRoots(const StringRefVector &paths) {
_syslibRoots = paths;
}
void MachOLinkingContext::addRpath(StringRef rpath) {
_rpaths.push_back(rpath);
}
void MachOLinkingContext::addModifiedSearchDir(StringRef libPath,
bool isSystemPath) {
bool addedModifiedPath = false;
// -syslibroot only applies to absolute paths.
if (libPath.startswith("/")) {
for (auto syslibRoot : _syslibRoots) {
SmallString<256> path(syslibRoot);
llvm::sys::path::append(path, libPath);
if (pathExists(path)) {
_searchDirs.push_back(path.str().copy(_allocator));
addedModifiedPath = true;
}
}
}
if (addedModifiedPath)
return;
// Finally, if only one -syslibroot is given, system paths which aren't in it
// get suppressed.
if (_syslibRoots.size() != 1 || !isSystemPath) {
if (pathExists(libPath)) {
_searchDirs.push_back(libPath);
}
}
}
void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath,
bool isSystemPath) {
bool pathAdded = false;
// -syslibroot only used with to absolute framework search paths.
if (fwPath.startswith("/")) {
for (auto syslibRoot : _syslibRoots) {
SmallString<256> path(syslibRoot);
llvm::sys::path::append(path, fwPath);
if (pathExists(path)) {
_frameworkDirs.push_back(path.str().copy(_allocator));
pathAdded = true;
}
}
}
// If fwPath found in any -syslibroot, then done.
if (pathAdded)
return;
// If only one -syslibroot, system paths not in that SDK are suppressed.
if (isSystemPath && (_syslibRoots.size() == 1))
return;
// Only use raw fwPath if that directory exists.
if (pathExists(fwPath))
_frameworkDirs.push_back(fwPath);
}
llvm::Optional<StringRef>
MachOLinkingContext::searchDirForLibrary(StringRef path,
StringRef libName) const {
SmallString<256> fullPath;
if (libName.endswith(".o")) {
// A request ending in .o is special: just search for the file directly.
fullPath.assign(path);
llvm::sys::path::append(fullPath, libName);
if (fileExists(fullPath))
return fullPath.str().copy(_allocator);
return llvm::None;
}
// Search for dynamic library
fullPath.assign(path);
llvm::sys::path::append(fullPath, Twine("lib") + libName + ".dylib");
if (fileExists(fullPath))
return fullPath.str().copy(_allocator);
// If not, try for a static library
fullPath.assign(path);
llvm::sys::path::append(fullPath, Twine("lib") + libName + ".a");
if (fileExists(fullPath))
return fullPath.str().copy(_allocator);
return llvm::None;
}
llvm::Optional<StringRef>
MachOLinkingContext::searchLibrary(StringRef libName) const {
SmallString<256> path;
for (StringRef dir : searchDirs()) {
llvm::Optional<StringRef> searchDir = searchDirForLibrary(dir, libName);
if (searchDir)
return searchDir;
}
return llvm::None;
}
llvm::Optional<StringRef>
MachOLinkingContext::findPathForFramework(StringRef fwName) const{
SmallString<256> fullPath;
for (StringRef dir : frameworkDirs()) {
fullPath.assign(dir);
llvm::sys::path::append(fullPath, Twine(fwName) + ".framework", fwName);
if (fileExists(fullPath))
return fullPath.str().copy(_allocator);
}
return llvm::None;
}
bool MachOLinkingContext::validateImpl() {
// TODO: if -arch not specified, look at arch of first .o file.
if (_currentVersion && _outputMachOType != MH_DYLIB) {
error("-current_version can only be used with dylibs");
return false;
}
if (_compatibilityVersion && _outputMachOType != MH_DYLIB) {
error("-compatibility_version can only be used with dylibs");
return false;
}
if (_deadStrippableDylib && _outputMachOType != MH_DYLIB) {
error("-mark_dead_strippable_dylib can only be used with dylibs");
return false;
}
if (!_bundleLoader.empty() && outputMachOType() != MH_BUNDLE) {
error("-bundle_loader can only be used with Mach-O bundles");
return false;
}
// If -exported_symbols_list used, all exported symbols must be defined.
if (_exportMode == ExportMode::whiteList) {
for (const auto &symbol : _exportedSymbols)
addInitialUndefinedSymbol(symbol.getKey());
}
// If -dead_strip, set up initial live symbols.
if (deadStrip()) {
// Entry point is live.
if (outputTypeHasEntry())
addDeadStripRoot(entrySymbolName());
// Lazy binding helper is live.
if (needsStubsPass())
addDeadStripRoot(binderSymbolName());
// If using -exported_symbols_list, make all exported symbols live.
if (_exportMode == ExportMode::whiteList) {
setGlobalsAreDeadStripRoots(false);
for (const auto &symbol : _exportedSymbols)
addDeadStripRoot(symbol.getKey());
}
}
addOutputFileDependency(outputPath());
return true;
}
void MachOLinkingContext::addPasses(PassManager &pm) {
// objc pass should be before layout pass. Otherwise test cases may contain
// no atoms which confuses the layout pass.
if (needsObjCPass())
mach_o::addObjCPass(pm, *this);
mach_o::addLayoutPass(pm, *this);
if (needsStubsPass())
mach_o::addStubsPass(pm, *this);
if (needsCompactUnwindPass())
mach_o::addCompactUnwindPass(pm, *this);
if (needsGOTPass())
mach_o::addGOTPass(pm, *this);
if (needsTLVPass())
mach_o::addTLVPass(pm, *this);
if (needsShimPass())
mach_o::addShimPass(pm, *this); // Shim pass must run after stubs pass.
}
Writer &MachOLinkingContext::writer() const {
if (!_writer)
_writer = createWriterMachO(*this);
return *_writer;
}
ErrorOr<std::unique_ptr<MemoryBuffer>>
MachOLinkingContext::getMemoryBuffer(StringRef path) {
addInputFileDependency(path);
ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr =
MemoryBuffer::getFileOrSTDIN(path);
if (std::error_code ec = mbOrErr.getError())
return ec;
std::unique_ptr<MemoryBuffer> mb = std::move(mbOrErr.get());
// If buffer contains a fat file, find required arch in fat buffer
// and switch buffer to point to just that required slice.
uint32_t offset;
uint32_t size;
if (sliceFromFatFile(mb->getMemBufferRef(), offset, size))
return MemoryBuffer::getFileSlice(path, size, offset);
return std::move(mb);
}
MachODylibFile* MachOLinkingContext::loadIndirectDylib(StringRef path) {
ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = getMemoryBuffer(path);
if (mbOrErr.getError())
return nullptr;
ErrorOr<std::unique_ptr<File>> fileOrErr =
registry().loadFile(std::move(mbOrErr.get()));
if (!fileOrErr)
return nullptr;
std::unique_ptr<File> &file = fileOrErr.get();
file->parse();
MachODylibFile *result = reinterpret_cast<MachODylibFile *>(file.get());
// Node object now owned by _indirectDylibs vector.
_indirectDylibs.push_back(std::move(file));
return result;
}
MachODylibFile* MachOLinkingContext::findIndirectDylib(StringRef path) {
// See if already loaded.
auto pos = _pathToDylibMap.find(path);
if (pos != _pathToDylibMap.end())
return pos->second;
// Search -L paths if of the form "libXXX.dylib"
std::pair<StringRef, StringRef> split = path.rsplit('/');
StringRef leafName = split.second;
if (leafName.startswith("lib") && leafName.endswith(".dylib")) {
// FIXME: Need to enhance searchLibrary() to only look for .dylib
auto libPath = searchLibrary(leafName);
if (libPath)
return loadIndirectDylib(libPath.getValue());
}
// Try full path with sysroot.
for (StringRef sysPath : _syslibRoots) {
SmallString<256> fullPath;
fullPath.assign(sysPath);
llvm::sys::path::append(fullPath, path);
if (pathExists(fullPath))
return loadIndirectDylib(fullPath);
}
// Try full path.
if (pathExists(path)) {
return loadIndirectDylib(path);
}
return nullptr;
}
uint32_t MachOLinkingContext::dylibCurrentVersion(StringRef installName) const {
auto pos = _pathToDylibMap.find(installName);
if (pos != _pathToDylibMap.end())
return pos->second->currentVersion();
else
return 0x10000; // 1.0
}
uint32_t MachOLinkingContext::dylibCompatVersion(StringRef installName) const {
auto pos = _pathToDylibMap.find(installName);
if (pos != _pathToDylibMap.end())
return pos->second->compatVersion();
else
return 0x10000; // 1.0
}
void MachOLinkingContext::createImplicitFiles(
std::vector<std::unique_ptr<File> > &result) {
// Add indirect dylibs by asking each linked dylib to add its indirects.
// Iterate until no more dylibs get loaded.
size_t dylibCount = 0;
while (dylibCount != _allDylibs.size()) {
dylibCount = _allDylibs.size();
for (MachODylibFile *dylib : _allDylibs) {
dylib->loadReExportedDylibs([this] (StringRef path) -> MachODylibFile* {
return findIndirectDylib(path); });
}
}
// Let writer add output type specific extras.
writer().createImplicitFiles(result);
// If undefinedMode is != error, add a FlatNamespaceFile instance. This will
// provide a SharedLibraryAtom for symbols that aren't defined elsewhere.
if (undefinedMode() != UndefinedMode::error) {
result.emplace_back(new mach_o::FlatNamespaceFile(*this));
_flatNamespaceFile = result.back().get();
}
}
void MachOLinkingContext::registerDylib(MachODylibFile *dylib,
bool upward) const {
std::lock_guard<std::mutex> lock(_dylibsMutex);
if (!llvm::count(_allDylibs, dylib))
_allDylibs.push_back(dylib);
_pathToDylibMap[dylib->installName()] = dylib;
// If path is different than install name, register path too.
if (!dylib->path().equals(dylib->installName()))
_pathToDylibMap[dylib->path()] = dylib;
if (upward)
_upwardDylibs.insert(dylib);
}
bool MachOLinkingContext::isUpwardDylib(StringRef installName) const {
for (MachODylibFile *dylib : _upwardDylibs) {
if (dylib->installName().equals(installName))
return true;
}
return false;
}
ArchHandler &MachOLinkingContext::archHandler() const {
if (!_archHandler)
_archHandler = ArchHandler::create(_arch);
return *_archHandler;
}
void MachOLinkingContext::addSectionAlignment(StringRef seg, StringRef sect,
uint16_t align) {
SectionAlign entry = { seg, sect, align };
_sectAligns.push_back(entry);
}
void MachOLinkingContext::addSectCreateSection(
StringRef seg, StringRef sect,
std::unique_ptr<MemoryBuffer> content) {
if (!_sectCreateFile) {
auto sectCreateFile = llvm::make_unique<mach_o::SectCreateFile>();
_sectCreateFile = sectCreateFile.get();
getNodes().push_back(llvm::make_unique<FileNode>(std::move(sectCreateFile)));
}
assert(_sectCreateFile && "sectcreate file does not exist.");
_sectCreateFile->addSection(seg, sect, std::move(content));
}
bool MachOLinkingContext::sectionAligned(StringRef seg, StringRef sect,
uint16_t &align) const {
for (const SectionAlign &entry : _sectAligns) {
if (seg.equals(entry.segmentName) && sect.equals(entry.sectionName)) {
align = entry.align;
return true;
}
}
return false;
}
void MachOLinkingContext::addExportSymbol(StringRef sym) {
// Support old crufty export lists with bogus entries.
if (sym.endswith(".eh") || sym.startswith(".objc_category_name_")) {
llvm::errs() << "warning: ignoring " << sym << " in export list\n";
return;
}
// Only i386 MacOSX uses old ABI, so don't change those.
if ((_os != OS::macOSX) || (_arch != arch_x86)) {
// ObjC has two differnent ABIs. Be nice and allow one export list work for
// both ABIs by renaming symbols.
if (sym.startswith(".objc_class_name_")) {
std::string abi2className("_OBJC_CLASS_$_");
abi2className += sym.substr(17);
_exportedSymbols.insert(copy(abi2className));
std::string abi2metaclassName("_OBJC_METACLASS_$_");
abi2metaclassName += sym.substr(17);
_exportedSymbols.insert(copy(abi2metaclassName));
return;
}
}
// FIXME: Support wildcards.
_exportedSymbols.insert(sym);
}
bool MachOLinkingContext::exportSymbolNamed(StringRef sym) const {
switch (_exportMode) {
case ExportMode::globals:
llvm_unreachable("exportSymbolNamed() should not be called in this mode");
break;
case ExportMode::whiteList:
return _exportedSymbols.count(sym);
case ExportMode::blackList:
return !_exportedSymbols.count(sym);
}
llvm_unreachable("_exportMode unknown enum value");
}
std::string MachOLinkingContext::demangle(StringRef symbolName) const {
// Only try to demangle symbols if -demangle on command line
if (!demangleSymbols())
return symbolName;
// Only try to demangle symbols that look like C++ symbols
if (!symbolName.startswith("__Z"))
return symbolName;
SmallString<256> symBuff;
StringRef nullTermSym = Twine(symbolName).toNullTerminatedStringRef(symBuff);
// Mach-O has extra leading underscore that needs to be removed.
const char *cstr = nullTermSym.data() + 1;
int status;
char *demangled = llvm::itaniumDemangle(cstr, nullptr, nullptr, &status);
if (demangled) {
std::string result(demangled);
// __cxa_demangle() always uses a malloc'ed buffer to return the result.
free(demangled);
return result;
}
return symbolName;
}
static void addDependencyInfoHelper(llvm::raw_fd_ostream *DepInfo,
char Opcode, StringRef Path) {
if (!DepInfo)
return;
*DepInfo << Opcode;
*DepInfo << Path;
*DepInfo << '\0';
}
std::error_code MachOLinkingContext::createDependencyFile(StringRef path) {
std::error_code ec;
_dependencyInfo = std::unique_ptr<llvm::raw_fd_ostream>(
new llvm::raw_fd_ostream(path, ec, llvm::sys::fs::OF_None));
if (ec) {
_dependencyInfo.reset();
return ec;
}
addDependencyInfoHelper(_dependencyInfo.get(), 0x00, "lld" /*FIXME*/);
return std::error_code();
}
void MachOLinkingContext::addInputFileDependency(StringRef path) const {
addDependencyInfoHelper(_dependencyInfo.get(), 0x10, path);
}
void MachOLinkingContext::addInputFileNotFound(StringRef path) const {
addDependencyInfoHelper(_dependencyInfo.get(), 0x11, path);
}
void MachOLinkingContext::addOutputFileDependency(StringRef path) const {
addDependencyInfoHelper(_dependencyInfo.get(), 0x40, path);
}
void MachOLinkingContext::appendOrderedSymbol(StringRef symbol,
StringRef filename) {
// To support sorting static functions which may have the same name in
// multiple .o files, _orderFiles maps the symbol name to a vector
// of OrderFileNode each of which can specify a file prefix.
OrderFileNode info;
if (!filename.empty())
info.fileFilter = copy(filename);
info.order = _orderFileEntries++;
_orderFiles[symbol].push_back(info);
}
bool
MachOLinkingContext::findOrderOrdinal(const std::vector<OrderFileNode> &nodes,
const DefinedAtom *atom,
unsigned &ordinal) {
const File *objFile = &atom->file();
assert(objFile);
StringRef objName = objFile->path();
std::pair<StringRef, StringRef> dirAndLeaf = objName.rsplit('/');
if (!dirAndLeaf.second.empty())
objName = dirAndLeaf.second;
for (const OrderFileNode &info : nodes) {
if (info.fileFilter.empty()) {
// Have unprefixed symbol name in order file that matches this atom.
ordinal = info.order;
return true;
}
if (info.fileFilter.equals(objName)) {
// Have prefixed symbol name in order file that matches atom's path.
ordinal = info.order;
return true;
}
}
return false;
}
bool MachOLinkingContext::customAtomOrderer(const DefinedAtom *left,
const DefinedAtom *right,
bool &leftBeforeRight) const {
// No custom sorting if no order file entries.
if (!_orderFileEntries)
return false;
// Order files can only order named atoms.
StringRef leftName = left->name();
StringRef rightName = right->name();
if (leftName.empty() || rightName.empty())
return false;
// If neither is in order file list, no custom sorter.
auto leftPos = _orderFiles.find(leftName);
auto rightPos = _orderFiles.find(rightName);
bool leftIsOrdered = (leftPos != _orderFiles.end());
bool rightIsOrdered = (rightPos != _orderFiles.end());
if (!leftIsOrdered && !rightIsOrdered)
return false;
// There could be multiple symbols with same name but different file prefixes.
unsigned leftOrder;
unsigned rightOrder;
bool foundLeft =
leftIsOrdered && findOrderOrdinal(leftPos->getValue(), left, leftOrder);
bool foundRight = rightIsOrdered &&
findOrderOrdinal(rightPos->getValue(), right, rightOrder);
if (!foundLeft && !foundRight)
return false;
// If only one is in order file list, ordered one goes first.
if (foundLeft != foundRight)
leftBeforeRight = foundLeft;
else
leftBeforeRight = (leftOrder < rightOrder);
return true;
}
static bool isLibrary(const std::unique_ptr<Node> &elem) {
if (FileNode *node = dyn_cast<FileNode>(const_cast<Node *>(elem.get()))) {
File *file = node->getFile();
return isa<SharedLibraryFile>(file) || isa<ArchiveLibraryFile>(file);
}
return false;
}
// The darwin linker processes input files in two phases. The first phase
// links in all object (.o) files in command line order. The second phase
// links in libraries in command line order.
// In this function we reorder the input files so that all the object files
// comes before any library file. We also make a group for the library files
// so that the Resolver will reiterate over the libraries as long as we find
// new undefines from libraries.
void MachOLinkingContext::finalizeInputFiles() {
std::vector<std::unique_ptr<Node>> &elements = getNodes();
llvm::stable_sort(elements, [](const std::unique_ptr<Node> &a,
const std::unique_ptr<Node> &b) {
return !isLibrary(a) && isLibrary(b);
});
size_t numLibs = std::count_if(elements.begin(), elements.end(), isLibrary);
elements.push_back(llvm::make_unique<GroupEnd>(numLibs));
}
llvm::Error MachOLinkingContext::handleLoadedFile(File &file) {
auto *machoFile = dyn_cast<MachOFile>(&file);
if (!machoFile)
return llvm::Error::success();
// Check that the arch of the context matches that of the file.
// Also set the arch of the context if it didn't have one.
if (_arch == arch_unknown) {
_arch = machoFile->arch();
} else if (machoFile->arch() != arch_unknown && machoFile->arch() != _arch) {
// Archs are different.
return llvm::make_error<GenericError>(file.path() +
Twine(" cannot be linked due to incompatible architecture"));
}
// Check that the OS of the context matches that of the file.
// Also set the OS of the context if it didn't have one.
if (_os == OS::unknown) {
_os = machoFile->OS();
} else if (machoFile->OS() != OS::unknown && machoFile->OS() != _os) {
// OSes are different.
return llvm::make_error<GenericError>(file.path() +
Twine(" cannot be linked due to incompatible operating systems"));
}
// Check that if the objc info exists, that it is compatible with the target
// OS.
switch (machoFile->objcConstraint()) {
case objc_unknown:
// The file is not compiled with objc, so skip the checks.
break;
case objc_gc_only:
case objc_supports_gc:
llvm_unreachable("GC support should already have thrown an error");
case objc_retainReleaseForSimulator:
// The file is built with simulator objc, so make sure that the context
// is also building with simulator support.
if (_os != OS::iOS_simulator)
return llvm::make_error<GenericError>(file.path() +
Twine(" cannot be linked. It contains ObjC built for the simulator"
" while we are linking a non-simulator target"));
assert((_objcConstraint == objc_unknown ||
_objcConstraint == objc_retainReleaseForSimulator) &&
"Must be linking with retain/release for the simulator");
_objcConstraint = objc_retainReleaseForSimulator;
break;
case objc_retainRelease:
// The file is built without simulator objc, so make sure that the
// context is also building without simulator support.
if (_os == OS::iOS_simulator)
return llvm::make_error<GenericError>(file.path() +
Twine(" cannot be linked. It contains ObjC built for a non-simulator"
" target while we are linking a simulator target"));
assert((_objcConstraint == objc_unknown ||
_objcConstraint == objc_retainRelease) &&
"Must be linking with retain/release for a non-simulator target");
_objcConstraint = objc_retainRelease;
break;
}
// Check that the swift version of the context matches that of the file.
// Also set the swift version of the context if it didn't have one.
if (!_swiftVersion) {
_swiftVersion = machoFile->swiftVersion();
} else if (machoFile->swiftVersion() &&
machoFile->swiftVersion() != _swiftVersion) {
// Swift versions are different.
return llvm::make_error<GenericError>("different swift versions");
}
return llvm::Error::success();
}
} // end namespace lld