1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
|
//===--------------- Toolchain+LinkerSupport.swift - Swift Linker Support -===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftOptions
import protocol TSCBasic.FileSystem
extension Toolchain {
// MARK: - Path computation
func computeSecondaryResourceDirPath(for triple: Triple, primaryPath: VirtualPath) -> VirtualPath? {
guard triple.isMacCatalyst else { return nil }
return primaryPath.parentDirectory.appending(component: "macosx")
}
func clangLibraryPath(
for targetInfo: FrontendTargetInfo,
parsedOptions: inout ParsedOptions
) throws -> VirtualPath {
var platform = targetInfo.target.triple.platformName(conflatingDarwin: true)!
// compiler-rt moved these Android sanitizers into `lib/linux/` a couple
// years ago, llvm/llvm-project@a68ccba, so look for them there instead.
if platform == "android" {
platform = "linux"
}
return VirtualPath.lookup(targetInfo.runtimeResourcePath.path)
.appending(components: "clang", "lib", platform)
}
func runtimeLibraryPaths(
for targetInfo: FrontendTargetInfo,
parsedOptions: inout ParsedOptions,
sdkPath: VirtualPath.Handle?,
isShared: Bool
) throws -> [VirtualPath] {
let triple = targetInfo.target.triple
let resourceDirPath = VirtualPath.lookup(targetInfo.runtimeResourcePath.path).appending(component: triple.platformName() ?? "")
var result = [resourceDirPath]
let secondaryResourceDir = computeSecondaryResourceDirPath(for: triple, primaryPath: resourceDirPath)
if let path = secondaryResourceDir {
result.append(path)
}
// Only Darwin places libraries directly in /sdk/usr/lib/swift/.
if triple.isDarwin, let sdkPath = sdkPath.map(VirtualPath.lookup) {
// If we added the secondary resource dir, we also need the iOSSupport directory.
if secondaryResourceDir != nil {
result.append(sdkPath.appending(components: "System", "iOSSupport", "usr", "lib", "swift"))
}
result.append(sdkPath.appending(components: "usr", "lib", "swift"))
}
return result
}
func runtimeLibraryExists(
for sanitizer: Sanitizer,
targetInfo: FrontendTargetInfo,
parsedOptions: inout ParsedOptions,
isShared: Bool
) throws -> Bool {
let runtimeName = try runtimeLibraryName(
for: sanitizer,
targetTriple: targetInfo.target.triple,
isShared: isShared
)
let path = try clangLibraryPath(
for: targetInfo,
parsedOptions: &parsedOptions
).appending(component: runtimeName)
return try fileSystem.exists(path)
}
func addLinkedLibArgs(
to commandLine: inout [Job.ArgTemplate],
parsedOptions: inout ParsedOptions
) {
for match in parsedOptions.arguments(for: .l) {
commandLine.appendFlag(match.option.spelling + match.argument.asSingle)
}
}
func addExtraClangLinkerArgs(
to commandLine: inout [Job.ArgTemplate],
parsedOptions: inout ParsedOptions
) throws {
// Because we invoke `clang` as the linker executable, we must still
// use `-Xlinker` for linker-specific arguments.
for linkerOpt in parsedOptions.arguments(for: .Xlinker) {
commandLine.appendFlag(.Xlinker)
commandLine.appendFlag(linkerOpt.argument.asSingle)
}
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)
}
}
// MARK: - Common argument routines
extension DarwinToolchain {
func addArgsToLinkStdlib(
to commandLine: inout [Job.ArgTemplate],
parsedOptions: inout ParsedOptions,
targetInfo: FrontendTargetInfo,
linkerOutputType: LinkOutputType,
fileSystem: FileSystem
) throws {
let targetTriple = targetInfo.target.triple
// Link compatibility libraries, if we're deploying back to OSes that
// have an older Swift runtime.
func addArgsForBackDeployLib(_ libName: String, forceLoad: Bool) throws {
let backDeployLibPath = VirtualPath.lookup(targetInfo.runtimeResourcePath.path)
.appending(components: targetTriple.platformName() ?? "", libName)
if try fileSystem.exists(backDeployLibPath) {
if forceLoad {
commandLine.append(.flag("-force_load"))
}
commandLine.appendPath(backDeployLibPath)
}
}
let experimentalFeatures = parsedOptions.arguments(for: .enableExperimentalFeature)
let embeddedEnabled = experimentalFeatures.map(\.argument).map(\.asSingle).contains("Embedded")
if !embeddedEnabled {
for compatibilityLib in targetInfo.target.compatibilityLibraries {
let shouldLink: Bool
switch compatibilityLib.filter {
case .all:
shouldLink = true
break
case .executable:
shouldLink = linkerOutputType == .executable
}
if shouldLink {
// Old frontends don't set forceLoad at all; assume it's true in that case
try addArgsForBackDeployLib("lib" + compatibilityLib.libraryName + ".a",
forceLoad: compatibilityLib.forceLoad ?? true)
}
}
}
// Add the runtime library link path, which is platform-specific and found
// relative to the compiler.
let runtimePaths = try runtimeLibraryPaths(
for: targetInfo,
parsedOptions: &parsedOptions,
sdkPath: targetInfo.sdkPath?.path,
isShared: true
)
for path in runtimePaths {
commandLine.appendFlag(.L)
commandLine.appendPath(path)
}
let rpaths = StdlibRpathRule(
parsedOptions: &parsedOptions,
targetInfo: targetInfo
)
for path in try rpaths.paths(runtimeLibraryPaths: runtimePaths) {
commandLine.appendFlag("-rpath")
commandLine.appendPath(path)
}
}
/// Represents the rpaths we need to add in order to find the
/// desired standard library at runtime.
fileprivate enum StdlibRpathRule {
/// Add a set of rpaths that will allow the compiler resource directory
/// to override Swift-in-the-OS dylibs.
case toolchain
/// Add an rpath that will search Swift-in-the-OS dylibs, but not
/// compiler resource directory dylibs.
case os
/// Do not add any rpaths at all.
case none
/// Determines the appropriate rule for the given set of options.
init(parsedOptions: inout ParsedOptions, targetInfo: FrontendTargetInfo) {
if parsedOptions.hasFlag(
positive: .toolchainStdlibRpath,
negative: .noToolchainStdlibRpath,
default: false
) {
// If the user has explicitly asked for a toolchain stdlib, we should
// provide one using -rpath. This used to be the default behaviour but it
// was considered annoying in at least the SwiftPM scenario (see
// https://bugs.swift.org/browse/SR-1967) and is obsolete in all scenarios
// of deploying for Swift-in-the-OS. We keep it here as an optional
// behaviour so that people downloading snapshot toolchains for testing new
// stdlibs will be able to link to the stdlib bundled in that toolchain.
self = .toolchain
}
else if !targetInfo.target.librariesRequireRPath ||
parsedOptions.hasArgument(.noStdlibRpath) {
// If targeting an OS with Swift in /usr/lib/swift, the LC_ID_DYLIB
// install_name the stdlib will be an absolute path like
// /usr/lib/swift/libswiftCore.dylib, and we do not need to provide an rpath
// at all.
//
// Also, if the user explicitly asks for no rpath entry, we assume they know
// what they're doing and do not add one here.
self = .none
}
else {
// The remaining cases are back-deploying (to OSs predating
// Swift-in-the-OS). In these cases, the stdlib will be giving us (via
// stdlib/linker-support/magic-symbols-for-install-name.c) an LC_ID_DYLIB
// install_name that is rpath-relative, like @rpath/libswiftCore.dylib.
//
// If we're linking an app bundle, it's possible there's an embedded stdlib
// in there, in which case we'd want to put @executable_path/../Frameworks
// in the rpath to find and prefer it, but (a) we don't know when we're
// linking an app bundle and (b) we probably _never_ will be because Xcode
// links using clang, not the swift driver.
//
// So that leaves us with the case of linking a command-line app. These are
// only supported by installing a secondary package that puts some frozen
// Swift-in-OS libraries in the /usr/lib/swift location. That's the best we
// can give for rpath, though it might fail at runtime if the support
// package isn't installed.
self = .os
}
}
func paths(runtimeLibraryPaths: [VirtualPath]) throws -> [VirtualPath] {
switch self {
case .toolchain:
return runtimeLibraryPaths
case .os:
return [.absolute(try .init(validating: "/usr/lib/swift"))]
case .none:
return []
}
}
}
}
|