I've written up a bug report about this here.
The problem is that the MSVC linker adds extra padding bytes after a data entry if incremental linking is turned on. This then throws off the Swift runtime when it tries to find out where the Swift metadata starts and where it ends because it isn't aware that the linker has inserted those extra bytes behind its back. The end result is that Swift code which tries to do anything that requires access to the metadata will crash because the padding bytes are misinterpreted as metadata.
Now a simple fix to this would be to ensure that:
a) all Swift runtime related libraries are linked with /incremental:no
b) all Swift executables and libraries that the Swift compiler generates are linked with /incremental:no
and I guess that doing this would be a good start in any case. However I'm not sure that this is really a good longterm fix and I'd prefer a fix that would allow the Swift runtime to deal with metadata sections that were generated with /incremental:yes since this option is automatically enabled if you generate a debug build. But I'm also not really sure on how to fix this properly and thus it would be helpful to hear the opinion of others before I start implementing something.
After reading through the Windows related runtime code, this is how the runtime finds its metadata stuff on Windows:
a) The compiler generates the metadata section just like on macOS / Linux but with shorter names and an extra suffix. E.g. .sw5rfst$B. The section is 16 bytes aligned.
b) The swiftest.obj file defines two extra sections per metadata section: .sw5rfst$A and .sw5rfst$C. Those sections are 8 byte aligned and they just contain a single 64bit variable each.
c) The linker merges the A, B and C sections in this order into a single .sw5rfst section with 16 byte alignment.
Now what happens when you link with /incremental:yes is that the linker adds an extra 256 0 bytes after the contents of .sw5rfst$A and .sw5rfst$C when it copies those guys into the final .sw5rfst section. It also adds 0 bytes after the contents of .sw5rfst$B but it sometimes adds way more than 256 0 bytes.
I can see two possible options to deal with this:
a) try to skip the padding bytes. This would work well for the padding bytes between .sw5rtst$A and .sw5rfst$B because the padding here is always 256 0 bytes followed alignment bytes to get to the next mod(16) == 0 address. But the distance between .sw5rfst$C and the end of the real metadata in .sw5rfst$B can be much more than 256 0 bytes. Now you could attempt to scan backwards and skip 0 bytes until you hit a non-zero, but this approach is very brittle because at least some sections may contain legitimately zeros as part of their metadata.
b) change the code that generates the metadata section in the compiler to spit out a marker at the beginning and the end of the metadata section. The linker won't add padding between those markers and the real metadata because the whole metadata blob is one data element to the linker. The runtime would then scan for those markers to find out what the real start and end of a metadata section is.
I'd prefer (b) over (a) because it would allow the runtime to find the start and end locations of its metadata without having to make assumptions about how many padding bytes the linker has inserted.
Opinions / better ideas?