diff --git a/Sources/SwiftDriver/Driver/Driver.swift b/Sources/SwiftDriver/Driver/Driver.swift index f9d9473e6..1cbc49a6b 100644 --- a/Sources/SwiftDriver/Driver/Driver.swift +++ b/Sources/SwiftDriver/Driver/Driver.swift @@ -838,6 +838,8 @@ public struct Driver { Self.validateSanitizerAddressUseOdrIndicatorFlag(&parsedOptions, diagnosticEngine: diagnosticsEngine, addressSanitizerEnabled: enabledSanitizers.contains(.address)) + Self.validateSanitizeStableABI(&parsedOptions, diagnosticEngine: diagnosticsEngine, addressSanitizerEnabled: enabledSanitizers.contains(.address)) + Self.validateSanitizerRecoverArgValues(&parsedOptions, diagnosticEngine: diagnosticsEngine, enabledSanitizers: enabledSanitizers) Self.validateSanitizerCoverageArgs(&parsedOptions, @@ -2434,14 +2436,15 @@ extension Driver { continue } + let stableAbi = sanitizer == .address && parsedOptions.contains(.sanitizeStableAbiEQ) // Support is determined by existence of the sanitizer library. // FIXME: Should we do this? This prevents cross-compiling with sanitizers // enabled. var sanitizerSupported = try toolchain.runtimeLibraryExists( - for: sanitizer, + for: stableAbi ? .address_stable_abi : sanitizer, targetInfo: targetInfo, parsedOptions: &parsedOptions, - isShared: sanitizer != .fuzzer + isShared: sanitizer != .fuzzer && !stableAbi ) if sanitizer == .thread { @@ -3057,6 +3060,17 @@ extension Driver { } } + private static func validateSanitizeStableABI( + _ parsedOptions: inout ParsedOptions, + diagnosticEngine: DiagnosticsEngine, + addressSanitizerEnabled: Bool + ) { + if (parsedOptions.hasArgument(.sanitizeStableAbiEQ) && !addressSanitizerEnabled) { + diagnosticEngine.emit( + .warning_option_requires_sanitizer(currentOption: .sanitizeStableAbiEQ, currentOptionValue: "", sanitizerRequired: .address)) + } + } + /// Validates the set of `-sanitize-recover={sanitizer}` arguments private static func validateSanitizerRecoverArgValues( _ parsedOptions: inout ParsedOptions, diff --git a/Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift b/Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift index d6d388ceb..fbe58aed0 100644 --- a/Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift +++ b/Sources/SwiftDriver/Jobs/DarwinToolchain+LinkerSupport.swift @@ -209,6 +209,10 @@ extension DarwinToolchain { .sorted() // Sort so we get a stable, testable order .joined(separator: ",") commandLine.appendFlag("-fsanitize=\(sanitizerNames)") + + if parsedOptions.contains(.sanitizeStableAbiEQ) { + commandLine.appendFlag("-fsanitize-stable-abi") + } } if parsedOptions.contains(.embedBitcode) { diff --git a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift index 9adbd3dfc..8e49aae46 100644 --- a/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift +++ b/Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift @@ -216,6 +216,7 @@ extension Driver { try commandLine.appendLast(.sanitizeEQ, from: &parsedOptions) try commandLine.appendLast(.sanitizeRecoverEQ, from: &parsedOptions) try commandLine.appendLast(.sanitizeAddressUseOdrIndicator, from: &parsedOptions) + try commandLine.appendLast(.sanitizeStableAbiEQ, from: &parsedOptions) try commandLine.appendLast(.sanitizeCoverageEQ, from: &parsedOptions) try commandLine.appendLast(.static, from: &parsedOptions) try commandLine.appendLast(.swiftVersion, from: &parsedOptions) diff --git a/Sources/SwiftDriver/Utilities/Sanitizer.swift b/Sources/SwiftDriver/Utilities/Sanitizer.swift index cd566b812..33f84c832 100644 --- a/Sources/SwiftDriver/Utilities/Sanitizer.swift +++ b/Sources/SwiftDriver/Utilities/Sanitizer.swift @@ -16,6 +16,9 @@ public enum Sanitizer: String, Hashable { /// Address sanitizer (ASan) case address + // Address sanitizer Stable ABI (ASan) + case address_stable_abi + /// Thread sanitizer (TSan) case thread @@ -34,6 +37,7 @@ public enum Sanitizer: String, Hashable { var libraryName: String { switch self { case .address: return "asan" + case .address_stable_abi: return "asan_abi" case .thread: return "tsan" case .undefinedBehavior: return "ubsan" case .fuzzer: return "fuzzer" diff --git a/Sources/SwiftOptions/Options.swift b/Sources/SwiftOptions/Options.swift index 864cdf9a2..73fc2797e 100644 --- a/Sources/SwiftOptions/Options.swift +++ b/Sources/SwiftOptions/Options.swift @@ -696,6 +696,7 @@ extension Option { public static let sanitizeAddressUseOdrIndicator: Option = Option("-sanitize-address-use-odr-indicator", .flag, attributes: [.helpHidden, .frontend, .noInteractive], helpText: "When using AddressSanitizer enable ODR indicator globals to avoid false ODR violation reports in partially sanitized programs at the cost of an increase in binary size") public static let sanitizeCoverageEQ: Option = Option("-sanitize-coverage=", .commaJoined, attributes: [.frontend, .noInteractive], metaVar: "", helpText: "Specify the type of coverage instrumentation for Sanitizers and additional options separated by commas") public static let sanitizeRecoverEQ: Option = Option("-sanitize-recover=", .commaJoined, attributes: [.frontend, .noInteractive], metaVar: "", helpText: "Specify which sanitizer runtime checks (see -sanitize=) will generate instrumentation that allows error recovery. Listed checks should be comma separated. Default behavior is to not allow error recovery.") + public static let sanitizeStableAbiEQ: Option = Option("-sanitize-stable-abi", .flag, attributes: [.frontend, .noInteractive], helpText: "ABI instrumentation for sanitizer runtime.") public static let sanitizeEQ: Option = Option("-sanitize=", .commaJoined, attributes: [.frontend, .noInteractive], metaVar: "", helpText: "Turn on runtime checks for erroneous behavior.") public static let saveOptimizationRecordPasses: Option = Option("-save-optimization-record-passes", .separate, attributes: [.frontend], metaVar: "", helpText: "Only include passes which match a specified regular expression in the generated optimization record (by default, include all passes)") public static let saveOptimizationRecordPath: Option = Option("-save-optimization-record-path", .separate, attributes: [.frontend, .argumentIsPath], helpText: "Specify the file name of any generated optimization record") @@ -1523,6 +1524,7 @@ extension Option { Option.sanitizeAddressUseOdrIndicator, Option.sanitizeCoverageEQ, Option.sanitizeRecoverEQ, + Option.sanitizeStableAbiEQ, Option.sanitizeEQ, Option.saveOptimizationRecordPasses, Option.saveOptimizationRecordPath, diff --git a/Tests/SwiftDriverTests/SwiftDriverTests.swift b/Tests/SwiftDriverTests/SwiftDriverTests.swift index 00d32bda6..d77a5d821 100644 --- a/Tests/SwiftDriverTests/SwiftDriverTests.swift +++ b/Tests/SwiftDriverTests/SwiftDriverTests.swift @@ -2731,6 +2731,27 @@ final class SwiftDriverTests: XCTestCase { } } + func testSanitizeStableAbi() throws { + do { + var driver = try Driver(args: ["swiftc", "-sanitize=address", "-sanitize-stable-abi", "Test.swift"]) + + let plannedJobs = try driver.planBuild().removingAutolinkExtractJobs() + XCTAssertEqual(plannedJobs.count, 2) + XCTAssertEqual(plannedJobs[0].kind, .compile) + XCTAssert(plannedJobs[0].commandLine.contains(.flag("-sanitize=address"))) + XCTAssert(plannedJobs[0].commandLine.contains(.flag("-sanitize-stable-abi"))) + + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fsanitize=address"))) + XCTAssert(plannedJobs[1].commandLine.contains(.flag("-fsanitize-stable-abi"))) + } + + do { + try assertDriverDiagnostics(args: ["swiftc","-sanitize-stable-abi", "Test.swift"]) { + $1.expect(.warning("option '-sanitize-stable-abi' has no effect when 'address' sanitizer is disabled. Use -sanitize=address to enable the sanitizer")) + } + } + } + func testADDITIONAL_SWIFT_DRIVER_FLAGS() throws { var env = ProcessEnv.vars env["ADDITIONAL_SWIFT_DRIVER_FLAGS"] = "-Xfrontend -unknown1 -Xfrontend -unknown2"