File: Toolchain%2BLinkerSupport.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (253 lines) | stat: -rw-r--r-- 9,410 bytes parent folder | download
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 []
      }
    }
  }

}