-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[WIP][SR-648] Add option to create statically linked binaries #4754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Stomping on system API with stubs like this feels brittle. If the binary is truly completely static, we have the full conformance table available statically, so the runtime could link against it. The registration stuff only exists to discover conformances from dylibs dynamically. |
2b63ee5
to
630b995
Compare
@jckarter Thanks for your comment, I have simplified the stub as the libdl functions are not actually used to find the conformance tables since the linker sets some symbols to point to them if linking statically and these are used instead. The registration stuff is then skipped entirely. libdl is only needed to resolve the dlopen() etc however it won't actually work in a static binary and it gives the following message at link time:
So I substituted them with some fatal error functions to catch any callers. I have no particular view as to linking libdl.a or using the stub functions as it will work either way although avoiding the warning message may help avoid user confusion |
Do you know what is triggering that warning? It would be nice if, in the static library configuration, we didn't touch dlopen at all. |
// externally | ||
const void *unused1 __attribute__ ((unused, visibility("internal"))) = pthread_self; | ||
const void *unused2 __attribute__ ((unused, visibility("internal"))) = pthread_key_create; | ||
const void *unused3 __attribute__ ((unused, visibility("internal"))) = pthread_once; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In libswiftCore.a those symbols are marked as unresolved weak:
nm -u ~/swift-test/usr/lib/swift_static/linux/libswiftCore.a |grep ' w '|sort -u
w __pthread_key_create
w pthread_once
w pthread_self
w sched_yield
And this causes gold to not link them at all so they end up having a linear address of 0x0 in the binary. Im not sure if this is a gold bug or working as intended but I couldnt find a linker option to force these symbols to be resolved so I added these three lines to make them into normal unresolved symbols
nm -u ~/swift-test/usr/lib/swift_static/linux/x86_64/static_stub.o
U abort
U fprintf
U fputc
U pthread_key_create
U pthread_once
U pthread_self
U stderr
The warning is embedded in libdl.a:
Its caused by the use of libdl in ProtocolConformance.cpp. Since libswiftCore.a can be statically linked into a dynamic binary (-static-stdlib) It still needs to be able to read the conformance data from any dynamically loaded modules. A possible solution, without creating libswiftCore.a and libswiftCoreWithoutDlopen.a, is:
Point 1. may be worth doing anyway since there is some duplication of code between the two files and it would move all of the platform specific code with all of its #ifdefs into its own file making it easier to read ProtocolConformance.cpp |
I agree, I like the idea of breaking the lookup code into its own .cpp file, letting the driver choose to link the fully-static or dynamic-loading version as needed by the build product. |
428940f
to
ec29d3f
Compare
- Move OS specific libdl calls from ProtocolConformance.cpp into SectionData.cpp - Remove duplicate defintions also contained in MetadataLookup.cpp
- Adds -static-executable option to swiftc to use along with -emit-executable - Only works on Linux for now, adds a stub to replace select libdl functions with a fatal error message to catch other libs that use dlopen() etc (eg libicu) to aid in debugging since they wont work on static binaries anyway and could just silently fail otherwise - Darwin doesnt support static binaries, see https://developer.apple.com/library/content/qa/qa1118/_index.html
ec29d3f
to
27a46bf
Compare
I refactored all of the lookup code into SectionData.cpp/SectionData.h which tidied up ProtocolConformance.cpp and MetadataLookup.cpp. This is the first commit which I will open as a separate PR since it doesn't add or remove any functionality but is a worthwhile cleanup anyway I did try creating 2 drivers for a DylibLookup.a and a StaticLookup.a however libicu still requires linking to libdl for dlopen() even though it doesn't use the functionality in my testing. Also trying to create two separate shared and static libs which contain different source files made the CMake files a mess of hard to maintain hacks to make it work correctly for platforms that can use it without adding extra complexity to platforms that can't. So I went back to just modifying the lookup code for static binaries and adding in some extra checks if new sections are added without the appropriate symbols being set, and the stub just catches any calls to dl* functions and aborts as these would need to be fixed |
If you're having problems getting the CMake scripts to look good, @erg might be able to help. We should look into fixing |
I agree that these libdl hacks arent the best solution.
So it can run without plugins although I don't know what functionality this breaks. Of course libicu is configured by the distro package maintainers so apart from building it as part of stdlib (which is a rather heavy solution just to enable 1 build option) Im not sure what can be done here The other fix needed is to remove the dladdr() from Errors.cpp for linux which I don't think works anyway (see SR-755) I'll close this PR for now, I can always rework it if these issues are fixed and there is still a requirement for static binaries in the future |
Thanks for looking into this, @spevans! To be clear, I think being able to emit static binaries would be awesome, and I hope I didn't discourage you by nitpicking too heavily. I doubt we rely on libicu's plugin loading feature. @dabrahams or @parkera would know for sure. |
Foundation doesn't do anything like that directly, but I'm unsure if something we call in ICU does it on its own. |
This is a proposed fix for SR-648 to produce static binaries with the package manager. If this is ok then I will add some test cases for it.
It allows static binaries to be created on supported platforms:
swiftc -emit-executable -static-executable -o main.static main.swift
It involves some linker magic to work around dlopen()/dlsym() not working on static binaries to fixup the conformance and metadata sections at startup
-emit-executable
libdl functions
https://developer.apple.com/library/content/qa/qa1118/_index.html