forked from OSchip/llvm-project
1099 lines
33 KiB
C++
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
|