Commits

Drew Crawford committed 7ae91eae3ce
Implement -static-stdlib for Linux (#1891) [Driver] implement -static-stdlib for Linux Implement the -static-stdlib driver flag for Linux, allowing the static linking of the standard library. This implementation largely follows the Darwin implementation in #1817, although some pecularities warrant extended discussion. The original "link with stdlib" implementation had some redundancies with getRuntimeLibraryPath; these redundancies are resolved, and the implementation alternates between getRuntimeLibraryPath and getStaticRuntimeLibraryPath cleanly as appropriate. A variety of libraries are required to link statically on Linux. The implementation currently dynamically links with them. We should probably support static linking of those as well, but I think that is beyond the scope of a -static-stdlib flag. The test coverage uses ldd here, as otool is not available on Linux. As a result, we currently have separate tests for Linux vs the other platforms; that isn't ideal, but it seems necessary. Perhaps the oddest part, and the one worth the most discussion, is the use of --dynamic-list. Inside stdlib/public/runtime/ProtocolConformances.cpp appears the following code: #elif defined(__ELF__) static int _addImageProtocolConformances(struct dl_phdr_info *info, size_t size, void *data) { // inspectArgs contains addImage*Block function and the section name InspectArgs *inspectArgs = reinterpret_cast<InspectArgs *>(data); void *handle; if (!info->dlpi_name || info->dlpi_name[0] == '\0') { handle = dlopen(nullptr, RTLD_LAZY); } else handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD); auto conformances = reinterpret_cast<const uint8_t*>( dlsym(handle, inspectArgs->sectionName)); The effect of this is to search for protocol_conformances_start inside the images. However, dlsym only finds symbols that exist in the dynamic table. Failure to find the protocol conformances can be diagnosed by a "hello world" program printing String(_core: Swift._StringCore(_baseAddress: Swift.OpaquePointer(_rawValue: (Opaque Value)), _countAndFlags: Swift.UInt(_value: (Opaque Value)), _owner: Swift.Optional<Swift.AnyObject>.none)) instead of "hello world". (And also by the test coverage in this commit.) Surprisingly, this behavior can still occur on ELF platforms even if `objdump -t` reports a valid `.protocol_conformances_start`. This is because `objdump -t` searches the global table, not the dynamic table, while dlsym only searches the dynamic table. To configure objdump to search only the dynamic table, use `-T`. Inquiring minds may wonder whether dynamically-linked programs (e.g. all Linux binaries up until now) also have a broken protocol conformance table on ELF. The answer is, surprisingly, no; I checked, and ordinary ELF programs are fine. The distinction is probably this, from the ld manpage: > the dynamic symbol table will normally contain only those symbols which are referenced by some dynamic object mentioned in the link. I think the linker sees `.protocol_conformances_start` inside libswiftCore.so and erroneously concludes the one in *the executable* is "referenced by some dynamic object" (e.g. the standard library). This behavior seems to save the dyanmically-linked executable from a broken protocol conformance table. I wonder if it would be wise to apply a similar fix to dynamically-linked programs to avoid relying on the linker "helping" us here, but that's out of scope of this commit. The linker manpage reflects that many people have been bitten by dlsym "surprise", and encourages the use of `--export-dynamic`: > If you use "dlopen" to load a dynamic object which needs to refer back > to the symbols defined by the program, rather than some other dynamic > object, then you will probably need to use [--export-dynamic] when > linking the program itself. However in this situation, the use of `--export-dynamic` causes the entire stdlib to be exported, which is not ideal. However, by combining with the `--exclude-libs ALL` argument, we avoid exporting the entire stdlib.