diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index ed257d58e8546..1b5c18b4d469b 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2026 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 @@ -722,13 +722,12 @@ void importer::getNormalInvocationArguments( 1})); // b } } else { - // Ideally we should turn this on for all Glibc targets that are actually - // using Glibc or a libc that respects that flag. This will cause some - // source breakage however (specifically with strerror_r()) on Linux - // without a workaround. - if (triple.isOSFuchsia() || triple.isAndroid() || triple.isMusl()) { - // Many of the modern libc features are hidden behind feature macros like - // _GNU_SOURCE or _XOPEN_SOURCE. + // Many of the modern libc features are hidden behind feature macros like + // _GNU_SOURCE or _XOPEN_SOURCE. Clang already predefines _GNU_SOURCE for + // these targets when the language is C++, so we define it unconditionally + // to minimize libc differences with and without C++ interop. + if (triple.isOSFuchsia() || triple.isAndroid() || triple.isMusl() || + triple.isOSGlibc()) { llvm::append_values(invocationArgStrs, "-D_GNU_SOURCE"); } diff --git a/stdlib/private/SwiftReflectionTest/CMakeLists.txt b/stdlib/private/SwiftReflectionTest/CMakeLists.txt index 8cbe7c8a6ee6d..ce74614f94ad1 100644 --- a/stdlib/private/SwiftReflectionTest/CMakeLists.txt +++ b/stdlib/private/SwiftReflectionTest/CMakeLists.txt @@ -13,7 +13,6 @@ if (SWIFT_INCLUDE_TESTS AND SWIFT_BUILD_DYNAMIC_STDLIB) add_swift_target_library(swiftSwiftReflectionTest ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB SwiftReflectionTest.swift SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} - SWIFT_COMPILE_FLAGS_LINUX -Xcc -D_GNU_SOURCE SWIFT_MODULE_DEPENDS_OSX ${swift_reflection_test_darwin_dependencies} SWIFT_MODULE_DEPENDS_IOS ${swift_reflection_test_darwin_dependencies} SWIFT_MODULE_DEPENDS_TVOS ${swift_reflection_test_darwin_dependencies} diff --git a/stdlib/public/Platform/CMakeLists.txt b/stdlib/public/Platform/CMakeLists.txt index 2b7c56c6066aa..5f57ff14d42a4 100644 --- a/stdlib/public/Platform/CMakeLists.txt +++ b/stdlib/public/Platform/CMakeLists.txt @@ -300,6 +300,38 @@ foreach(sdk ${SWIFT_SDKS}) endif() endforeach() endforeach() + +# Put the API notes file in place. +if("LINUX" IN_LIST SWIFT_SDKS OR "FREEBSD" IN_LIST SWIFT_SDKS OR + "OPENBSD" IN_LIST SWIFT_SDKS OR "CYGWIN" IN_LIST SWIFT_SDKS OR + "HAIKU" IN_LIST SWIFT_SDKS) + set(glibc_apinotes_source "SwiftGlibc.apinotes") + add_custom_command_target( + copy_glibc_apinotes_resource + COMMAND + "${CMAKE_COMMAND}" "-E" "make_directory" ${SWIFTLIB_DIR}/apinotes ${SWIFTSTATICLIB_DIR}/apinotes + COMMAND + "${CMAKE_COMMAND}" "-E" "copy_if_different" + "${CMAKE_CURRENT_SOURCE_DIR}/${glibc_apinotes_source}" ${SWIFTLIB_DIR}/apinotes + COMMAND + "${CMAKE_COMMAND}" "-E" "copy_if_different" + "${CMAKE_CURRENT_SOURCE_DIR}/${glibc_apinotes_source}" ${SWIFTSTATICLIB_DIR}/apinotes + OUTPUT + ${SWIFTLIB_DIR}/apinotes/${glibc_apinotes_source} + ${SWIFTSTATICLIB_DIR}/apinotes/${glibc_apinotes_source} + COMMENT "Copying SwiftGlibc API notes to resource directories") + + list(APPEND glibc_modulemap_target_list ${copy_glibc_apinotes_resource}) + swift_install_in_component(FILES "${glibc_apinotes_source}" + DESTINATION "lib/swift/apinotes" + COMPONENT sdk-overlay) + if(SWIFT_BUILD_STATIC_STDLIB) + swift_install_in_component(FILES "${glibc_apinotes_source}" + DESTINATION "lib/swift_static/apinotes" + COMPONENT sdk-overlay) + endif() +endif() + add_custom_target(glibc_modulemap DEPENDS ${glibc_modulemap_target_list}) set_property(TARGET glibc_modulemap PROPERTY FOLDER "Miscellaneous") add_dependencies(sdk-overlay glibc_modulemap) diff --git a/stdlib/public/Platform/Glibc.swift.gyb b/stdlib/public/Platform/Glibc.swift.gyb index d36044f48dc49..e6b7d65d9dba4 100644 --- a/stdlib/public/Platform/Glibc.swift.gyb +++ b/stdlib/public/Platform/Glibc.swift.gyb @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2026 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 @@ -83,3 +83,90 @@ nonisolated(unsafe) public var stderr: UnsafeMutablePointer! { _swift_stdlib_stderr().assumingMemoryBound(to: FILE.self) } #endif + +#if os(Linux) +// Glibc spells 'fd_set''s property as 'fds_bits' when '_XOPEN_SOURCE' is +// defined, or as '__fds_bits' otherwise. Allow code written before we defined +// '_GNU_SOURCE' to refer to the latter. +%{ + import textwrap + # declares the bit set as '__fd_mask fds_bits[1024 / N]', + # where '__fd_mask' is a 'long' and N is the bit width of 'long'. + FD_SET_BITS = 1024 + widths = [(bits, FD_SET_BITS // bits) for bits in (64, 32)] +}% +// We must qualify which version of `fd_set` is being extended. +extension SwiftGlibc.fd_set { +% for bits, count in widths: +#if _pointerBitWidth(_${bits}) + @available(swift, deprecated: 7, renamed: "fds_bits") + @export(implementation) + public var __fds_bits: ( +${textwrap.fill(', '.join(['Int'] * count), width = 74, + initial_indent = ' ', subsequent_indent = ' ')} + ) { + get { fds_bits } + set { fds_bits = newValue } + } +#endif +% end +} +#endif // os(Linux) + +#if os(Linux) +// The first parameter of these functions is 'int' in Glibc, unless +// '__USE_GNU' is defined, when it is an enumeration instead. Swift can't +// promote the int parameters to the enumeration, so we add overloads +// with the type signature expected by existing callers. +@_alwaysEmitIntoClient +public func getitimer( + _ which: CInt, _ value: UnsafeMutablePointer! +) -> CInt { + SwiftGlibc.getitimer( + __itimer_which(rawValue: UInt32(bitPattern: which)), value) +} + +@_alwaysEmitIntoClient +public func setitimer( + _ which: CInt, _ new: UnsafePointer!, + _ old: UnsafeMutablePointer! +) -> CInt { + SwiftGlibc.setitimer( + __itimer_which(rawValue: UInt32(bitPattern: which)), new, old) +} + +@_alwaysEmitIntoClient +public func getpriority(_ which: CInt, _ who: id_t) -> CInt { + SwiftGlibc.getpriority( + __priority_which(rawValue: UInt32(bitPattern: which)), who) +} + +@_alwaysEmitIntoClient +public func setpriority(_ which: CInt, _ who: id_t, _ priority: CInt) -> CInt { + SwiftGlibc.setpriority( + __priority_which(rawValue: UInt32(bitPattern: which)), who, priority) +} + +@_alwaysEmitIntoClient +public func getrlimit( + _ resource: CInt, _ rlimits: UnsafeMutablePointer +) -> CInt { + SwiftGlibc.getrlimit( + __rlimit_resource(rawValue: UInt32(bitPattern: resource)), rlimits) +} + +@_alwaysEmitIntoClient +public func setrlimit( + _ resource: CInt, _ rlimits: UnsafePointer +) -> CInt { + SwiftGlibc.setrlimit( + __rlimit_resource(rawValue: UInt32(bitPattern: resource)), rlimits) +} + +@_alwaysEmitIntoClient +public func getrusage( + _ who: CInt, _ usage: UnsafeMutablePointer! +) -> CInt { + SwiftGlibc.getrusage(__rusage_who(rawValue: who), usage) +} +#endif // os(Linux) diff --git a/stdlib/public/Platform/SwiftGlibc.apinotes b/stdlib/public/Platform/SwiftGlibc.apinotes new file mode 100644 index 0000000000000..55af56cb3471e --- /dev/null +++ b/stdlib/public/Platform/SwiftGlibc.apinotes @@ -0,0 +1,38 @@ +--- +Name: SwiftGlibc +Functions: +# Clang's ABI passes a transparent union as its first member. The following +# rewrites the union parameter type as the first member type, so that +# these functions import as they did before we defined '_GNU_SOURCE'. +- Name: accept + Parameters: + - Position: 1 + Type: "struct sockaddr * _Null_unspecified" +- Name: accept4 + Parameters: + - Position: 1 + Type: "struct sockaddr * _Null_unspecified" +- Name: bind + Parameters: + - Position: 1 + Type: "const struct sockaddr * _Null_unspecified" +- Name: connect + Parameters: + - Position: 1 + Type: "const struct sockaddr * _Null_unspecified" +- Name: getpeername + Parameters: + - Position: 1 + Type: "struct sockaddr * _Null_unspecified" +- Name: getsockname + Parameters: + - Position: 1 + Type: "struct sockaddr * _Null_unspecified" +- Name: recvfrom + Parameters: + - Position: 4 + Type: "struct sockaddr * _Null_unspecified" +- Name: sendto + Parameters: + - Position: 4 + Type: "const struct sockaddr * _Null_unspecified" diff --git a/stdlib/public/RuntimeModule/modules/OS/Linux.h b/stdlib/public/RuntimeModule/modules/OS/Linux.h deleted file mode 100644 index 716bc9635fafd..0000000000000 --- a/stdlib/public/RuntimeModule/modules/OS/Linux.h +++ /dev/null @@ -1,42 +0,0 @@ -//===--- Linux.h - Linux specifics ------------------------------*- C++ -*-===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2023 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 -// -//===----------------------------------------------------------------------===// -// -// Linux specific includes and declarations. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_BACKTRACING_LINUX_H -#define SWIFT_BACKTRACING_LINUX_H -#ifdef __linux__ - -#define _GNU_SOURCE -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -ssize_t process_vm_readv(pid_t pid, - const struct iovec *local_iov, - unsigned long liovcnt, - const struct iovec *remote_iov, - unsigned long riovcnt, - unsigned long flags); - -#ifdef __cplusplus -} // extern "C" -#endif - -#endif // __linux__ -#endif // SWIFT_BACKTRACING_LINUX_H - diff --git a/test/ClangImporter/linux-sdk-macros.swift b/test/ClangImporter/linux-sdk-macros.swift new file mode 100644 index 0000000000000..bf83571d398a4 --- /dev/null +++ b/test/ClangImporter/linux-sdk-macros.swift @@ -0,0 +1,14 @@ +// Platforms whose libc hides modern interfaces behind feature macros get +// _GNU_SOURCE defined for the importer. +// RUN: %swift -target x86_64-unknown-linux-gnu -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-GNU-SOURCE +// RUN: %swift -target x86_64-swift-linux-musl -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-GNU-SOURCE +// RUN: %swift -target aarch64-unknown-linux-android28 -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-GNU-SOURCE + +// Clang predefines _GNU_SOURCE for C++, so the two modes agree. +// RUN: %swift -target x86_64-unknown-linux-gnu -typecheck %s -parse-stdlib -cxx-interoperability-mode=default -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-GNU-SOURCE + +// Platforms that don't use glibc shouldn't be affected. +// RUN: %swift -target x86_64-unknown-freebsd -typecheck %s -parse-stdlib -dump-clang-diagnostics 2>&1 | %FileCheck %s -check-prefix CHECK-NO-GNU-SOURCE + +// CHECK-GNU-SOURCE: -D_GNU_SOURCE +// CHECK-NO-GNU-SOURCE-NOT: -D_GNU_SOURCE diff --git a/validation-test/stdlib/Glibc.swift b/validation-test/stdlib/Glibc.swift index 1b004367078a8..cdb287ddc12b9 100644 --- a/validation-test/stdlib/Glibc.swift +++ b/validation-test/stdlib/Glibc.swift @@ -34,4 +34,64 @@ GlibcIoctlConstants.test("tty ioctl constants availability") { let aConstant = TIOCSTI } + +// Test spellings introduced when '_GNU_SOURCE' is defined. +var GlibcGNUSource = TestSuite("GlibcGNUSource") + +GlibcGNUSource.test("accept4 address parameter") { + let accept4Signature: ( + CInt, UnsafeMutablePointer?, UnsafeMutablePointer?, CInt + ) -> CInt + accept4Signature = accept4 +} + +GlibcGNUSource.test("getrlimit accepts CInt") { + var rl = rlimit() + + expectEqual(0, getrlimit(__rlimit_resource_t(RLIMIT_CORE.rawValue), &rl)) + // use the compatibility overload: + expectEqual(0, getrlimit(CInt(RLIMIT_CORE.rawValue), &rl)) +} + +GlibcGNUSource.test("getrusage accepts a negative CInt") { + var ru = rusage() + + expectEqual(0, getrusage(__rusage_who_t(RUSAGE_CHILDREN.rawValue), &ru)) + // use the compatibility overload: + expectEqual(0, getrusage(CInt(RUSAGE_CHILDREN.rawValue), &ru)) +} + +GlibcGNUSource.test("getpriority accepts CInt") { + errno = 0 + _ = getpriority(__priority_which_t(PRIO_PROCESS.rawValue), 0) + expectEqual(0, errno) + + // use the compatibility overload: + errno = 0 + _ = getpriority(CInt(PRIO_PROCESS.rawValue), 0) + expectEqual(0, errno) +} + +GlibcGNUSource.test("getitimer accepts CInt") { + var itv = itimerval() + + expectEqual(0, getitimer(__itimer_which_t(ITIMER_REAL.rawValue), &itv)) + // use the compatibility overload: + expectEqual(0, getitimer(CInt(ITIMER_REAL.rawValue), &itv)) +} + +GlibcGNUSource.test("fd_set __fds_bits") { + var set = SwiftGlibc.fd_set() + // .fds_bits is the property name when _GNU_SOURCE is defined + set.fds_bits.3 = 42 + + // .__fds_bits is the property name without _GNU_SOURCE, + // made available with an extension in the Glibc overlay. + expectTrue(type(of: set.__fds_bits) == type(of: set.fds_bits)) + expectEqual(42, set.__fds_bits.3) + + set.__fds_bits.7 = 99 + expectEqual(99, set.fds_bits.7) +} + runAllTests() diff --git a/validation-test/stdlib/GlibcParameterTypes.swift b/validation-test/stdlib/GlibcParameterTypes.swift new file mode 100644 index 0000000000000..65c0970b107cd --- /dev/null +++ b/validation-test/stdlib/GlibcParameterTypes.swift @@ -0,0 +1,131 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 +// +//===----------------------------------------------------------------------===// + +// RUN: %target-typecheck-verify-swift +// REQUIRES: OS=linux-gnu + +// Defining '_GNU_SOURCE' changes the declared type of some Glibc parameters, +// and the spelling of some members. Ensure that the affected declarations +// accept the types and names expected by existing callers, to avoid source +// breaks as much as possible. +// +// This is a compatibility test; it is expected to succeed whether or not +// '_GNU_SOURCE' was defined when building the Glibc module. + +import Glibc + +// MARK: - Address parameters +// +// These became typed as a '__transparent_union__' union rather than as +// 'sockaddr *'. The parameter is rewritten as 'sockaddr *' in apinotes. + +func acceptConnections(_ fd: CInt) { + var address = sockaddr() + var length = socklen_t(MemoryLayout.size) + + _ = accept(fd, &address, &length) + _ = accept(fd, nil, nil) +} + +func nameSockets(_ fd: CInt) { + var address = sockaddr() + var length = socklen_t(MemoryLayout.size) + + _ = getsockname(fd, &address, &length) + _ = getpeername(fd, &address, &length) +} + +func connectSockets(_ fd: CInt) { + var address = sockaddr() + let length = socklen_t(MemoryLayout.size) + + _ = bind(fd, &address, length) + _ = connect(fd, &address, length) +} + +func transfer(_ fd: CInt) { + var address = sockaddr() + var addressLength = socklen_t(MemoryLayout.size) + var buffer = [UInt8](repeating: 0, count: 8) + + _ = sendto(fd, &buffer, buffer.count, 0, &address, addressLength) + _ = recvfrom(fd, &buffer, buffer.count, 0, &address, &addressLength) + _ = recvfrom(fd, &buffer, buffer.count, 0, nil, nil) +} + +// MARK: - Enumerated parameters +// +// The following functions's first parameter is usually declared as 'int', +// but under '__USE_GNU' it becomes an enumeration. The Glibc overlay adds +// 'CInt' overloads for compatibility. + +func resourceLimits() { + var limit = rlimit(rlim_cur: 1, rlim_max: 1) + + _ = getrlimit(CInt(RLIMIT_CORE.rawValue), &limit) + _ = setrlimit(CInt(RLIMIT_CORE.rawValue), &limit) +} + +func resourceUsage() { + var usage = rusage() + + _ = getrusage(CInt(RUSAGE_SELF.rawValue), &usage) + _ = getrusage(CInt(RUSAGE_CHILDREN.rawValue), &usage) +} + +func schedulingPriority() { + _ = getpriority(CInt(PRIO_PROCESS.rawValue), 0) + _ = setpriority(CInt(PRIO_PROCESS.rawValue), 0, 0) +} + +func intervalTimers() { + var timer = itimerval() + + _ = getitimer(CInt(ITIMER_REAL.rawValue), &timer) + _ = setitimer(CInt(ITIMER_REAL.rawValue), &timer, nil) +} + +func typedefSpelling(_ pid: CInt) { + var limit = rlimit(rlim_cur: 1, rlim_max: 1) + + _ = setrlimit(__rlimit_resource_t(RLIMIT_CORE.rawValue), &limit) + _ = setpriority(__priority_which_t(PRIO_PROCESS.rawValue), UInt32(pid), 0) +} + +func integerLiterals() { + var usage = rusage() + + _ = getrusage(0, &usage) +} + +// Test the imported signatures, so that a partially applied reference remains +// compatible. These function types are from before we defined '_GNU_SOURCE'. +func signatures() { + let _: (CInt, UnsafeMutablePointer) -> CInt = getrlimit + let _: (CInt, UnsafePointer) -> CInt = setrlimit + let _: (CInt, UnsafeMutablePointer?) -> CInt = getrusage + let _: (CInt, id_t) -> CInt = getpriority + let _: (CInt, id_t, CInt) -> CInt = setpriority + let _: (CInt, UnsafeMutablePointer?) -> CInt = getitimer + let _: (CInt, UnsafePointer?, UnsafeMutablePointer?) -> CInt = setitimer +} + +// MARK: - Structure members +// +// Test the presence of struct members renamed by _GNU_SOURCE. + +func fileDescriptorSets() { + var set = fd_set() + + let _: Int = set.__fds_bits.3 + set.__fds_bits.3 = 42 +}