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
|
//===--------------- WebAssemblyToolchain+LinkerSupport.swift -------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 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 func TSCBasic.lookupExecutablePath
import protocol TSCBasic.FileSystem
import struct TSCBasic.AbsolutePath
extension WebAssemblyToolchain {
public func addPlatformSpecificLinkerArgs(
to commandLine: inout [Job.ArgTemplate],
parsedOptions: inout ParsedOptions,
linkerOutputType: LinkOutputType,
inputs: [TypedVirtualPath],
outputFile: VirtualPath,
shouldUseInputFileList: Bool,
lto: LTOKind?,
sanitizers: Set<Sanitizer>,
targetInfo: FrontendTargetInfo
) throws -> ResolvedTool {
let targetTriple = targetInfo.target.triple
switch linkerOutputType {
case .dynamicLibrary:
throw Error.dynamicLibrariesUnsupportedForTarget(targetTriple.triple)
case .executable:
if !targetTriple.triple.isEmpty {
commandLine.appendFlag("-target")
commandLine.appendFlag(targetTriple.triple)
}
// Select the linker to use.
if let linkerArg = parsedOptions.getLastArgument(.useLd)?.asSingle {
commandLine.appendFlag("-fuse-ld=\(linkerArg)")
}
if let arg = parsedOptions.getLastArgument(.ldPath)?.asSingle {
commandLine.append(.joinedOptionAndPath("--ld-path=", try VirtualPath(path: arg)))
}
// Configure the toolchain.
//
// By default use the system `clang` to perform the link. We use `clang` for
// the driver here because we do not wish to select a particular C++ runtime.
// Furthermore, until C++ interop is enabled, we cannot have a dependency on
// C++ code from pure Swift code. If linked libraries are C++ based, they
// should properly link C++. In the case of static linking, the user can
// explicitly specify the C++ runtime to link against. This is particularly
// important for platforms like android where as it is a Linux platform, the
// default C++ runtime is `libstdc++` which is unsupported on the target but
// as the builds are usually cross-compiled from Linux, libstdc++ is going to
// be present. This results in linking the wrong version of libstdc++
// generating invalid binaries. It is also possible to use different C++
// runtimes than the default C++ runtime for the platform (e.g. libc++ on
// Windows rather than msvcprt). When C++ interop is enabled, we will need to
// surface this via a driver flag. For now, opt for the simpler approach of
// just using `clang` and avoid a dependency on the C++ runtime.
var clangPath = try getToolPath(.clang)
if let toolsDirPath = parsedOptions.getLastArgument(.toolsDirectory) {
// FIXME: What if this isn't an absolute path?
let toolsDir = try AbsolutePath(validating: toolsDirPath.asSingle)
// If there is a clang in the toolchain folder, use that instead.
if let tool = lookupExecutablePath(filename: "clang", searchPaths: [toolsDir]) {
clangPath = tool
}
// Look for binutils in the toolchain folder.
commandLine.appendFlag("-B")
commandLine.appendPath(toolsDir)
}
guard !parsedOptions.hasArgument(.noStaticStdlib, .noStaticExecutable) else {
throw Error.dynamicLibrariesUnsupportedForTarget(targetTriple.triple)
}
let runtimePaths = try runtimeLibraryPaths(
for: targetInfo,
parsedOptions: &parsedOptions,
sdkPath: targetInfo.sdkPath?.path,
isShared: false
)
if !parsedOptions.hasArgument(.nostartfiles) {
let swiftrtPath = VirtualPath.lookup(targetInfo.runtimeResourcePath.path)
.appending(
components: targetTriple.platformName() ?? "",
targetTriple.archName,
"swiftrt.o"
)
commandLine.appendPath(swiftrtPath)
}
let inputFiles: [Job.ArgTemplate] = inputs.compactMap { input in
// Autolink inputs are handled specially
if input.type == .autolink {
return .responseFilePath(input.file)
} else if input.type == .object {
return .path(input.file)
} else {
return nil
}
}
commandLine.append(contentsOf: inputFiles)
if let path = targetInfo.sdkPath?.path {
commandLine.appendFlag("--sysroot")
commandLine.appendPath(VirtualPath.lookup(path))
}
// Add the runtime library link paths.
for path in runtimePaths {
commandLine.appendFlag(.L)
commandLine.appendPath(path)
}
// Link the standard library and dependencies.
let linkFilePath: VirtualPath =
VirtualPath.lookup(targetInfo.runtimeResourcePath.path)
.appending(components: targetTriple.platformName() ?? "",
"static-executable-args.lnk")
guard try fileSystem.exists(linkFilePath) else {
throw Error.missingExternalDependency(linkFilePath.name)
}
commandLine.append(.responseFilePath(linkFilePath))
// Pass down an optimization level
if let optArg = mapOptimizationLevelToClangArg(from: &parsedOptions) {
commandLine.appendFlag(optArg)
}
// Explicitly pass the target to the linker
commandLine.appendFlag("--target=\(targetTriple.triple)")
// WebAssembly doesn't reserve low addresses as its ABI. But without
// "extra inhabitants" of the pointer representation, runtime performance
// and memory footprint are significantly degraded. So we reserve the
// low addresses to use them as extra inhabitants by telling the lowest
// valid address to the linker.
// The value of lowest valid address, called "global base", must be always
// synchronized with `SWIFT_ABI_WASM32_LEAST_VALID_POINTER` defined in
// apple/swift's runtime library.
commandLine.appendFlag(.Xlinker)
commandLine.appendFlag("--global-base=4096")
// Delegate to Clang for sanitizers. It will figure out the correct linker
// options.
guard sanitizers.isEmpty else {
throw Error.sanitizersUnsupportedForTarget(targetTriple.triple)
}
guard !parsedOptions.hasArgument(.profileGenerate) else {
throw Error.profilingUnsupportedForTarget(targetTriple.triple)
}
// Run clang++ in verbose mode if "-v" is set
try commandLine.appendLast(.v, from: &parsedOptions)
// These custom arguments should be right before the object file at the
// end.
try commandLine.appendAllExcept(
includeList: [.linkerOption],
excludeList: [.l],
from: &parsedOptions
)
addLinkedLibArgs(to: &commandLine, parsedOptions: &parsedOptions)
try addExtraClangLinkerArgs(to: &commandLine, parsedOptions: &parsedOptions)
// This should be the last option, for convenience in checking output.
commandLine.appendFlag(.o)
commandLine.appendPath(outputFile)
return try resolvedTool(.clang, pathOverride: clangPath)
case .staticLibrary:
// We're using 'ar' as a linker
commandLine.appendFlag("crs")
commandLine.appendPath(outputFile)
commandLine.append(contentsOf: inputs.map { .path($0.file) })
return try resolvedTool(.staticLinker(lto))
}
}
}
|