Towards `-static-stdlib` support on Windows

Windows has partially supported static linking for a while now. It explicitly requires that modules declare their type at compile time: libraries are assumed to be dynamic unless otherwise indicated via the -static flag. This is then serialised to the swiftmodule and wired through to IRGen to properly generate code with the appropriate DLL Storage.

While this allows for all user modules to be statically linked, it still was insufficient to enable static linking of the standard library. There has been a renewed push recently to enable the use of -static-stdlib on Windows. At this point, the issues in IRGen and DependencyScanning have been worked through. This lifts the restriction at the level of code generation.

In order to deal with the collision of import libraries changes to the library naming conventions have been instituted. This is in accordance to examples from within the MSVC distributions. Akin to libucrt.lib being the static distribution for UCRT and ucrt.lib being the import library, the driver assumes that the same naming convention applies to only Swift libraries autolinked through import statements. As this is relatively new, this has not been integrated into CMake, but is something that the user can easily workaround by adding -D CMAKE_STATIC_LIBRARY_PREFIX_Swift=lib when configuring the project.

Assuming that the proper flags are used and users use the feature as intended, I believe that in addition to supporting statically linking the standard library here, this will formally introduce support for -experimental-hermetic-seal-at-link. The -static ensures that symbols are not re-exported from the consuming module, -static-stdlib will also statically link the standard library, and it is UB to dynamically link against any other Swift library with -static-stdlib as that would involve multiple copies of the Swift standard library in the same address space.

Unresolved Items

  1. -nostartfiles needs to be supported in the C++ driver (:white_check_mark: this is actively being worked on)

  2. a new variant of the swiftrt.obj (at least on Windows) must be formed for the static library variant (:white_check_mark: this is actively being worked on - introducing swiftrtT.obj)

  3. We would need a static build of Foundation (:white_check_mark: this is actively being worked on)

  4. We would need a static build of libdispatch

  5. The concurrency runtime needs to be taught how to deal with a static libdispatch.

  6. The driver needs learn about the new swifrtT.obj

  7. The standard library build needs to be adjusted to enable building a static library variant for distribution. Note that this static library would still dynamically link against ucrt/msvcrt/msvcprt/vcruntime.

  8. We need to package and distribute the new runtime build.

  9. How do we ensure that -static-stdlib does not link against any other Swift libraries?

  10. Should -static-stdlib impact the BlocksRuntime? This is an extension for the C language and thus might need to be shared for the scenario where you may have a C host using Blocks which links against a statically linked Swift binary that uses the BlocksRuntime indirectly. I am tempted to say that BlocksRuntime should remain a dynamically linked component as it is a language extension, unlike GCD.

  11. Should -static-stdlib be synonymous with -static-sdk? The SDK comprises of more than just the standard library. It contains at the very least:

    1. _Concurrency
    2. _StringProcessing
    3. Distributed
    4. Foundation
    5. Dispatch, BlocksRuntime
    6. XCTest
    7. Testing

    What makes this complicated is the fact that BlocksRuntime is something which could be dynamically linked against a fully sealed Swift containing binary. The restriction with static linking is that you may not have two copies of the standard library in the address space. However, the Blocks extension is a C extension and you may have a dynamically linked executable with a statically linked Swift runtime that also uses Blocks. Currently, we do not have a means for isolation of static vs dynamic linking of runtimes. To further complicate matters, BlocksRuntime on Windows was expected to always be dynamically linked (until recently - I just lifted this restriction with the introduction of -static-libclosure to clang upstream).

Unaddressed Items

  1. -static-executable will not be addressed with these changes. -static-executable requires a subsequent set of changes to build a static swift standard library with /MT to get a static ucrt/msvcrt and a static msvcprt/vcruntime.
  2. Swift Testing is not supported with static binaries/static standard library AFAIK.
  3. Although we use the thick swiftmodule format with SDKs on Windows, we cannot resolve the conflict between the static and dynamic variants currently. Technically, this shouldn't be a problem as long as we keep the lib/swift and lib/static_swift distinction, but that currently is not very well handled on Windows.

I really wish that we named the registrar srtm{,S,T}.obj as it is just the metadata registration

CC: @etcwilde @al45tair @Douglas_Gregor @kubamracek

10 Likes

There's a non-zero chance this could go away entirely on Windows and we could build something much closer to what Darwin does. It's the ELF implementation that really needs this file, not PE/COFF.

1 Like

I think that would be wonderful. However, I don’t think that waiting for that is something we should do. This work was re-prioritized due to the need for bootstrapping the compiler.

Oh, sure—it shouldn't hold up your work here. :slightly_smiling_face: