diff options
Diffstat (limited to 'lib/ReaderWriter/MachO/MachOLinkingContext.cpp')
-rw-r--r-- | lib/ReaderWriter/MachO/MachOLinkingContext.cpp | 216 |
1 files changed, 170 insertions, 46 deletions
diff --git a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp index 1c97c5a39d3f..05375f145d34 100644 --- a/lib/ReaderWriter/MachO/MachOLinkingContext.cpp +++ b/lib/ReaderWriter/MachO/MachOLinkingContext.cpp @@ -35,6 +35,7 @@ #endif using lld::mach_o::ArchHandler; +using lld::mach_o::MachOFile; using lld::mach_o::MachODylibFile; using namespace llvm::MachO; @@ -75,6 +76,35 @@ bool MachOLinkingContext::parsePackedVersion(StringRef str, uint32_t &result) { 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 }, @@ -139,44 +169,52 @@ bool MachOLinkingContext::sliceFromFatFile(MemoryBufferRef mb, uint32_t &offset, return mach_o::normalized::sliceFromFatFile(mb, _arch, offset, size); } -MachOLinkingContext::MachOLinkingContext() - : _outputMachOType(MH_EXECUTE), _outputMachOTypeStatic(false), - _doNothing(false), _pie(false), _arch(arch_unknown), _os(OS::macOSX), - _osMinVersion(0), _pageZeroSize(0), _pageSize(4096), _baseAddress(0), - _stackSize(0), _compatibilityVersion(0), _currentVersion(0), - _flatNamespace(false), _undefinedMode(UndefinedMode::error), - _deadStrippableDylib(false), _printAtoms(false), _testingFileUsage(false), - _keepPrivateExterns(false), _demangle(false), _archHandler(nullptr), - _exportMode(ExportMode::globals), - _debugInfoMode(DebugInfoMode::addDebugMap), _orderFileEntries(0), - _flatNamespaceFile(nullptr) {} - -MachOLinkingContext::~MachOLinkingContext() {} +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) { + uint32_t minOSVersion, + bool exportDynamicSymbols) { _outputMachOType = type; _arch = arch; _os = os; _osMinVersion = minOSVersion; // If min OS not specified on command line, use reasonable defaults. - 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; + // 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; + } } } @@ -217,9 +255,10 @@ void MachOLinkingContext::configure(HeaderFileType type, Arch arch, OS os, case OS::unknown: break; } + setGlobalsAreDeadStripRoots(exportDynamicSymbols); break; case llvm::MachO::MH_DYLIB: - setGlobalsAreDeadStripRoots(true); + setGlobalsAreDeadStripRoots(exportDynamicSymbols); break; case llvm::MachO::MH_BUNDLE: break; @@ -325,6 +364,11 @@ bool MachOLinkingContext::needsCompactUnwindPass() const { } } +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) @@ -368,9 +412,11 @@ bool MachOLinkingContext::minOS(StringRef mac, StringRef iOS) const { return false; return _osMinVersion >= parsedVersion; case OS::unknown: - break; + // 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("target not configured for iOS or MacOSX"); + llvm_unreachable("invalid OS enum"); } bool MachOLinkingContext::addEntryPointLoadCommand() const { @@ -484,7 +530,7 @@ void MachOLinkingContext::addFrameworkSearchDir(StringRef fwPath, _frameworkDirs.push_back(fwPath); } -ErrorOr<StringRef> +llvm::Optional<StringRef> MachOLinkingContext::searchDirForLibrary(StringRef path, StringRef libName) const { SmallString<256> fullPath; @@ -494,7 +540,7 @@ MachOLinkingContext::searchDirForLibrary(StringRef path, llvm::sys::path::append(fullPath, libName); if (fileExists(fullPath)) return fullPath.str().copy(_allocator); - return make_error_code(llvm::errc::no_such_file_or_directory); + return llvm::None; } // Search for dynamic library @@ -509,21 +555,23 @@ MachOLinkingContext::searchDirForLibrary(StringRef path, if (fileExists(fullPath)) return fullPath.str().copy(_allocator); - return make_error_code(llvm::errc::no_such_file_or_directory); + return llvm::None; } -ErrorOr<StringRef> MachOLinkingContext::searchLibrary(StringRef libName) const { +llvm::Optional<StringRef> +MachOLinkingContext::searchLibrary(StringRef libName) const { SmallString<256> path; for (StringRef dir : searchDirs()) { - ErrorOr<StringRef> ec = searchDirForLibrary(dir, libName); - if (ec) - return ec; + llvm::Optional<StringRef> searchDir = searchDirForLibrary(dir, libName); + if (searchDir) + return searchDir; } - return make_error_code(llvm::errc::no_such_file_or_directory); + return llvm::None; } -ErrorOr<StringRef> MachOLinkingContext::findPathForFramework(StringRef fwName) const{ +llvm::Optional<StringRef> +MachOLinkingContext::findPathForFramework(StringRef fwName) const{ SmallString<256> fullPath; for (StringRef dir : frameworkDirs()) { fullPath.assign(dir); @@ -532,7 +580,7 @@ ErrorOr<StringRef> MachOLinkingContext::findPathForFramework(StringRef fwName) c return fullPath.str().copy(_allocator); } - return make_error_code(llvm::errc::no_such_file_or_directory); + return llvm::None; } bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) { @@ -589,6 +637,10 @@ bool MachOLinkingContext::validateImpl(raw_ostream &diagnostics) { } 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); @@ -656,9 +708,8 @@ MachODylibFile* MachOLinkingContext::findIndirectDylib(StringRef path) { if (leafName.startswith("lib") && leafName.endswith(".dylib")) { // FIXME: Need to enhance searchLibrary() to only look for .dylib auto libPath = searchLibrary(leafName); - if (!libPath.getError()) { - return loadIndirectDylib(libPath.get()); - } + if (libPath) + return loadIndirectDylib(libPath.getValue()); } // Try full path with sysroot. @@ -990,4 +1041,77 @@ void MachOLinkingContext::finalizeInputFiles() { 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(); + + // 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(); +} + } // end namespace lld |