Skip to content

Commit 26895b4

Browse files
authored
Merge pull request #1111 from keith/ks/switch-to-using-clang-as-linker-on-darwin
Switch to using clang as linker on darwin
2 parents 3d59f0a + 7d910ed commit 26895b4

File tree

5 files changed

+120
-411
lines changed

5 files changed

+120
-411
lines changed

Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift

Lines changed: 40 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -47,153 +47,6 @@ extension DarwinToolchain {
4747
return try findXcodeClangLibPath("arc")
4848
}
4949

50-
internal func addLTOLibArgs(to commandLine: inout [Job.ArgTemplate]) throws {
51-
if let path = try findXcodeClangLibPath("libLTO.dylib") {
52-
commandLine.appendFlag("-lto_library")
53-
commandLine.appendPath(path)
54-
}
55-
}
56-
57-
func addLinkRuntimeLibraryRPath(
58-
to commandLine: inout [Job.ArgTemplate],
59-
parsedOptions: inout ParsedOptions,
60-
targetInfo: FrontendTargetInfo,
61-
darwinLibName: String
62-
) throws {
63-
// Adding the rpaths might negatively interact when other rpaths are involved,
64-
// so we should make sure we add the rpaths last, after all user-specified
65-
// rpaths. This is currently true from this place, but we need to be
66-
// careful if this function is ever called before user's rpaths are emitted.
67-
assert(darwinLibName.hasSuffix(".dylib"), "must be a dynamic library")
68-
69-
// Add @executable_path to rpath to support having the dylib copied with
70-
// the executable.
71-
commandLine.appendFlag("-rpath")
72-
commandLine.appendFlag("@executable_path")
73-
74-
// Add the path to the resource dir to rpath to support using the dylib
75-
// from the default location without copying.
76-
77-
78-
let clangPath = try clangLibraryPath(
79-
for: targetInfo,
80-
parsedOptions: &parsedOptions)
81-
commandLine.appendFlag("-rpath")
82-
commandLine.appendPath(clangPath)
83-
}
84-
85-
func addLinkSanitizerLibArgsForDarwin(
86-
to commandLine: inout [Job.ArgTemplate],
87-
parsedOptions: inout ParsedOptions,
88-
targetInfo: FrontendTargetInfo,
89-
sanitizer: Sanitizer,
90-
isShared: Bool
91-
) throws {
92-
// Sanitizer runtime libraries requires C++.
93-
commandLine.appendFlag("-lc++")
94-
// Add explicit dependency on -lc++abi, as -lc++ doesn't re-export
95-
// all RTTI-related symbols that are used.
96-
commandLine.appendFlag("-lc++abi")
97-
98-
let sanitizerName = try runtimeLibraryName(
99-
for: sanitizer,
100-
targetTriple: targetInfo.target.triple,
101-
isShared: isShared
102-
)
103-
try addLinkRuntimeLibrary(
104-
named: sanitizerName,
105-
to: &commandLine,
106-
for: targetInfo,
107-
parsedOptions: &parsedOptions
108-
)
109-
110-
if isShared {
111-
try addLinkRuntimeLibraryRPath(
112-
to: &commandLine,
113-
parsedOptions: &parsedOptions,
114-
targetInfo: targetInfo,
115-
darwinLibName: sanitizerName
116-
)
117-
}
118-
}
119-
120-
private func addProfileGenerationArgs(
121-
to commandLine: inout [Job.ArgTemplate],
122-
parsedOptions: inout ParsedOptions,
123-
targetInfo: FrontendTargetInfo
124-
) throws {
125-
guard parsedOptions.hasArgument(.profileGenerate) else { return }
126-
let clangPath = try clangLibraryPath(for: targetInfo,
127-
parsedOptions: &parsedOptions)
128-
129-
for runtime in targetInfo.target.triple.darwinPlatform!.profileLibraryNameSuffixes {
130-
let clangRTPath = clangPath
131-
.appending(component: "libclang_rt.profile_\(runtime).a")
132-
commandLine.appendPath(clangRTPath)
133-
}
134-
}
135-
136-
private func addPlatformVersionArg(to commandLine: inout [Job.ArgTemplate],
137-
for triple: Triple, sdkPath: VirtualPath.Handle?) {
138-
assert(triple.isDarwin)
139-
let platformName = triple.darwinPlatform!.linkerPlatformName
140-
let platformVersion = triple.darwinLinkerPlatformVersion
141-
let sdkVersion: Version
142-
if let sdkPath = sdkPath,
143-
let sdkInfo = getTargetSDKInfo(sdkPath: sdkPath) {
144-
sdkVersion = sdkInfo.sdkVersion(for: triple)
145-
} else {
146-
sdkVersion = Version(0, 0, 0)
147-
}
148-
149-
commandLine.append(.flag("-platform_version"))
150-
commandLine.append(.flag(platformName))
151-
commandLine.append(.flag(platformVersion.description))
152-
commandLine.append(.flag(sdkVersion.description))
153-
}
154-
155-
private func addDeploymentTargetArgs(
156-
to commandLine: inout [Job.ArgTemplate],
157-
targetTriple: Triple,
158-
targetVariantTriple: Triple?,
159-
sdkPath: VirtualPath.Handle?
160-
) {
161-
addPlatformVersionArg(to: &commandLine, for: targetTriple, sdkPath: sdkPath)
162-
if let variantTriple = targetVariantTriple {
163-
assert(targetTriple.isValidForZipperingWithTriple(variantTriple))
164-
addPlatformVersionArg(to: &commandLine, for: variantTriple,
165-
sdkPath: sdkPath)
166-
}
167-
}
168-
169-
private func addArgsToLinkARCLite(
170-
to commandLine: inout [Job.ArgTemplate],
171-
parsedOptions: inout ParsedOptions,
172-
targetTriple: Triple
173-
) throws {
174-
guard parsedOptions.hasFlag(
175-
positive: .linkObjcRuntime,
176-
negative: .noLinkObjcRuntime,
177-
default: !targetTriple.supports(.nativeARC)
178-
) else {
179-
return
180-
}
181-
182-
guard let arcLiteLibPath = try findARCLiteLibPath(),
183-
let platformName = targetTriple.platformName() else {
184-
return
185-
}
186-
let fullLibPath = arcLiteLibPath
187-
.appending(components: "libarclite_\(platformName).a")
188-
189-
commandLine.appendFlag("-force_load")
190-
commandLine.appendPath(fullLibPath)
191-
192-
// Arclite depends on CoreFoundation.
193-
commandLine.appendFlag(.framework)
194-
commandLine.appendFlag("CoreFoundation")
195-
}
196-
19750
/// Adds the arguments necessary to link the files from the given set of
19851
/// options for a Darwin platform.
19952
public func addPlatformSpecificLinkerArgs(
@@ -213,7 +66,7 @@ extension DarwinToolchain {
21366
case .dynamicLibrary:
21467
// Same as an executable, but with the -dylib flag
21568
linkerTool = .dynamicLinker
216-
commandLine.appendFlag("-dylib")
69+
commandLine.appendFlag("-dynamiclib")
21770
addLinkInputs(shouldUseInputFileList: shouldUseInputFileList,
21871
commandLine: &commandLine,
21972
inputs: inputs,
@@ -280,8 +133,7 @@ extension DarwinToolchain {
280133
commandLine.appendPath(fileList)
281134
if linkerOutputType != .staticLibrary {
282135
for module in inputModules {
283-
commandLine.append(.flag("-add_ast_path"))
284-
commandLine.append(.path(module))
136+
commandLine.append(.joinedOptionAndPath("-Wl,-add_ast_path,", module))
285137
}
286138
}
287139

@@ -291,7 +143,7 @@ extension DarwinToolchain {
291143
commandLine.append(contentsOf: inputs.flatMap {
292144
(path: TypedVirtualPath) -> [Job.ArgTemplate] in
293145
if path.type == .swiftModule && linkerOutputType != .staticLibrary {
294-
return [.flag("-add_ast_path"), .path(path.file)]
146+
return [.joinedOptionAndPath("-Wl,-add_ast_path,", path.file)]
295147
} else if path.type == .object {
296148
return [.path(path.file)]
297149
} else if path.type == .tbd {
@@ -311,39 +163,16 @@ extension DarwinToolchain {
311163
sanitizers: Set<Sanitizer>,
312164
linkerOutputType: LinkOutputType,
313165
lto: LTOKind?) throws {
314-
// FIXME: If we used Clang as a linker instead of going straight to ld,
315-
// we wouldn't have to replicate a bunch of Clang's logic here.
316-
317-
// Always link the regular compiler_rt if it's present. Note that the
318-
// regular libclang_rt.a uses a fat binary for device and simulator; this is
319-
// not true for all compiler_rt build products.
320-
//
321-
// Note: Normally we'd just add this unconditionally, but it's valid to build
322-
// Swift and use it as a linker without building compiler_rt.
323-
let targetTriple = targetInfo.target.triple
324-
let darwinPlatformSuffix =
325-
targetTriple.darwinPlatform!.with(.device)!.libraryNameSuffix
326-
let compilerRTPath =
327-
try clangLibraryPath(
328-
for: targetInfo,
329-
parsedOptions: &parsedOptions)
330-
.appending(component: "libclang_rt.\(darwinPlatformSuffix).a")
331-
if try fileSystem.exists(compilerRTPath) {
332-
commandLine.append(.path(compilerRTPath))
333-
}
334-
335-
try addArgsToLinkARCLite(
336-
to: &commandLine,
337-
parsedOptions: &parsedOptions,
338-
targetTriple: targetTriple
339-
)
166+
if let lto = lto {
167+
switch lto {
168+
case .llvmFull:
169+
commandLine.appendFlag("-flto=full")
170+
case .llvmThin:
171+
commandLine.appendFlag("-flto=thin")
172+
}
340173

341-
if lto != nil {
342174
if let arg = parsedOptions.getLastArgument(.ltoLibrary)?.asSingle {
343-
commandLine.appendFlag("-lto_library")
344-
commandLine.appendPath(try VirtualPath(path: arg))
345-
} else {
346-
try addLTOLibArgs(to: &commandLine)
175+
commandLine.append(.joinedOptionAndPath("-Wl,-lto_library,", try VirtualPath(path: arg)))
347176
}
348177
}
349178

@@ -354,43 +183,44 @@ extension DarwinToolchain {
354183
}
355184

356185
if parsedOptions.contains(.enableAppExtension) {
357-
commandLine.appendFlag("-application_extension")
186+
commandLine.appendFlag("-fapplication-extension")
358187
}
359188

360189
// Linking sanitizers will add rpaths, which might negatively interact when
361190
// other rpaths are involved, so we should make sure we add the rpaths after
362191
// all user-specified rpaths.
363-
for sanitizer in sanitizers {
364-
if sanitizer == .fuzzer {
365-
guard linkerOutputType == .executable else { continue }
366-
}
367-
try addLinkSanitizerLibArgsForDarwin(
368-
to: &commandLine,
369-
parsedOptions: &parsedOptions,
370-
targetInfo: targetInfo,
371-
sanitizer: sanitizer,
372-
isShared: sanitizer != .fuzzer
373-
)
192+
if linkerOutputType == .executable && !sanitizers.isEmpty {
193+
let sanitizerNames = sanitizers
194+
.map { $0.rawValue }
195+
.sorted() // Sort so we get a stable, testable order
196+
.joined(separator: ",")
197+
commandLine.appendFlag("-fsanitize=\(sanitizerNames)")
374198
}
375199

376-
if parsedOptions.contains(.embedBitcode) ||
377-
parsedOptions.contains(.embedBitcodeMarker) {
378-
commandLine.appendFlag("-bitcode_bundle")
200+
if parsedOptions.contains(.embedBitcode) {
201+
commandLine.appendFlag("-fembed-bitcode")
202+
} else if parsedOptions.contains(.embedBitcodeMarker) {
203+
commandLine.appendFlag("-fembed-bitcode=marker")
379204
}
380205

381206
// Add the SDK path
382207
if let sdkPath = targetInfo.sdkPath?.path {
383-
commandLine.appendFlag("-syslibroot")
208+
commandLine.appendFlag("--sysroot")
384209
commandLine.appendPath(VirtualPath.lookup(sdkPath))
385210
}
386211

387212
commandLine.appendFlags(
213+
"-fobjc-link-runtime",
388214
"-lobjc",
389215
"-lSystem"
390216
)
391217

392-
commandLine.appendFlag("-arch")
393-
commandLine.appendFlag(targetTriple.archName)
218+
let targetTriple = targetInfo.target.triple
219+
commandLine.appendFlag("--target=\(targetTriple.triple)")
220+
if let variantTriple = targetInfo.targetVariant?.triple {
221+
assert(targetTriple.isValidForZipperingWithTriple(variantTriple))
222+
commandLine.appendFlag("-darwin-target-variant=\(variantTriple.triple)")
223+
}
394224

395225
// On Darwin, we only support libc++.
396226
if parsedOptions.contains(.enableExperimentalCxxInterop) {
@@ -405,19 +235,9 @@ extension DarwinToolchain {
405235
fileSystem: fileSystem
406236
)
407237

408-
try addProfileGenerationArgs(
409-
to: &commandLine,
410-
parsedOptions: &parsedOptions,
411-
targetInfo: targetInfo
412-
)
413-
414-
let targetVariantTriple = targetInfo.targetVariant?.triple
415-
addDeploymentTargetArgs(
416-
to: &commandLine,
417-
targetTriple: targetTriple,
418-
targetVariantTriple: targetVariantTriple,
419-
sdkPath: targetInfo.sdkPath?.path
420-
)
238+
if parsedOptions.hasArgument(.profileGenerate) {
239+
commandLine.appendFlag("-fprofile-generate")
240+
}
421241

422242
// These custom arguments should be right before the object file at the
423243
// end.
@@ -427,7 +247,13 @@ extension DarwinToolchain {
427247
from: &parsedOptions
428248
)
429249
addLinkedLibArgs(to: &commandLine, parsedOptions: &parsedOptions)
430-
try commandLine.appendAllArguments(.Xlinker, from: &parsedOptions)
250+
// Because we invoke `clang` as the linker executable, we must still
251+
// use `-Xlinker` for linker-specific arguments.
252+
for linkerOpt in parsedOptions.arguments(for: .Xlinker) {
253+
commandLine.appendFlag(.Xlinker)
254+
commandLine.appendFlag(linkerOpt.argument.asSingle)
255+
}
256+
try commandLine.appendAllArguments(.XclangLinker, from: &parsedOptions)
431257
}
432258
}
433259

Sources/SwiftDriver/Jobs/Toolchain+LinkerSupport.swift

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,6 @@ extension Toolchain {
5656
return result
5757
}
5858

59-
func addLinkRuntimeLibrary(
60-
named name: String,
61-
to commandLine: inout [Job.ArgTemplate],
62-
for targetInfo: FrontendTargetInfo,
63-
parsedOptions: inout ParsedOptions
64-
) throws {
65-
let path = try clangLibraryPath(
66-
for: targetInfo,
67-
parsedOptions: &parsedOptions)
68-
.appending(component: name)
69-
commandLine.appendPath(path)
70-
}
71-
7259
func runtimeLibraryExists(
7360
for sanitizer: Sanitizer,
7461
targetInfo: FrontendTargetInfo,

Sources/SwiftDriver/Toolchains/DarwinToolchain.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public final class DarwinToolchain: Toolchain {
6464
case .swiftCompiler:
6565
return try lookup(executable: "swift-frontend")
6666
case .dynamicLinker:
67-
return try lookup(executable: "ld")
67+
return try lookup(executable: "clang")
6868
case .staticLinker:
6969
return try lookup(executable: "libtool")
7070
case .dsymutil:

Tests/SwiftDriverTests/JobExecutorTests.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,11 +179,11 @@ final class JobExecutorTests: XCTestCase {
179179
.path(.temporary(RelativePath("foo.o"))),
180180
.path(.temporary(RelativePath("main.o"))),
181181
.path(.absolute(try toolchain.clangRT.get())),
182-
"-syslibroot", .path(.absolute(try toolchain.sdk.get())),
183-
"-lobjc", "-lSystem", "-arch", .flag(hostTriple.archName),
182+
"--sysroot", .path(.absolute(try toolchain.sdk.get())),
183+
"-lobjc", "-lSystem", .flag("--target=\(hostTriple.triple)"),
184184
"-L", .path(.absolute(try toolchain.resourcesDirectory.get())),
185185
"-L", .path(.absolute(try toolchain.sdkStdlib(sdk: toolchain.sdk.get()))),
186-
"-rpath", "/usr/lib/swift", "-macosx_version_min", "10.14.0", "-o",
186+
"-rpath", "/usr/lib/swift", "-o",
187187
.path(.relative(RelativePath("main"))),
188188
],
189189
inputs: [
@@ -249,12 +249,10 @@ final class JobExecutorTests: XCTestCase {
249249
tool: try toolchain.resolvedTool(.dynamicLinker),
250250
commandLine: [
251251
.path(.temporary(RelativePath("main.o"))),
252-
.path(.absolute(try toolchain.clangRT.get())),
253-
"-syslibroot", .path(.absolute(try toolchain.sdk.get())),
254-
"-lobjc", "-lSystem", "-arch", .flag(hostTriple.archName),
252+
"--sysroot", .path(.absolute(try toolchain.sdk.get())),
253+
"-lobjc", "-lSystem", .flag("--target=\(hostTriple.triple)"),
255254
"-L", .path(.absolute(try toolchain.resourcesDirectory.get())),
256255
"-L", .path(.absolute(try toolchain.sdkStdlib(sdk: toolchain.sdk.get()))),
257-
"-rpath", "/usr/lib/swift", "-macosx_version_min", "10.14.0",
258256
"-o", .path(.absolute(exec)),
259257
],
260258
inputs: [

0 commit comments

Comments
 (0)