Hi,
Ive been looking at SR-648 (producing static binaries using swiftPM) and Ive
managed to get a static binary built on Linux/ELF. It may also work on other ELF
platforms although Ive not tested them. It doesnt currently apply to COFF or MachO
There are 4 issues:
1. -static-stdlib links in libswiftCore.a as its help text says but nothing
else. To link in all of the other .a files they need to be listed in the
ld command line including libicudata.a which is used by libicu. The list
is: libswiftCore.a, libicui18n.a, libicuuc.a, libicudata.a, libdl.a and
libpthread.a
2. The clang++ command line needs to be modified to use -static and remove
the -rpath and other dynamic options
3. Some symbols have the weak attribute set and dont get linked in by gold
so a stub is needed just to force the linker to link them, the function
doesnt actually need to be run. I couldnt find a gold option that forced
them to be resolved otherwise
4. Because the binary is static, libdl and the dl functions dlopen, dlsym
etc dont actually work. A patch needs to be applied to swift
(ProtocolConformance.cpp and MetadataLookup.cpp) that use some symbols
set by the linker to access the swift2_protocol_conformances and
swift2_type_metadata sections at startup. These would only be set when
building a static binary and bypass the libdl functions at startup.
The only other issue is that libdl would still be linked in and in future
other code that uses dl* to load data etc would fail. So it be may that
a minimal replacement of these functions to log an error is used instead
of libdl. If this approach looks ok, I'll start working on a PR that adds
a '-static' option to swiftpm to try and resolve SR-648 (for Linux)
Ive done a rough patch for swift/stdlib with some debug fprintf()
@ Example change to support static conformance and metatype data sections (rough patch with excess debug) · GitHub
Example usage
$ cat main.swift
print("hello")
$ cat main.autolink
-Xlinker --defsym=__swift2_protocol_conformances_start=.swift2_protocol_conformances_start
-Xlinker --defsym=__swift2_type_metadata_start=.swift2_type_metadata_start
static_stub.o
-lswiftCore
-licui18n
-licuuc
-licudata
-ldl
-lpthread
$ cat static_stub.c
#include <stdlib.h> // For NULL
#include <bits/pthreadtypes.h>
int pthread_once (pthread_once_t *once_control, void (*init_routine) (void));
int pthread_key_create (pthread_key_t *key, void (*destr) (void *));
pthread_t pthread_self (void);
void
_unused_func_to_force_linking()
{
pthread_once(NULL, NULL);
pthread_key_create(NULL, NULL);
pthread_self();
}
# Normal dynamic binary
$ swiftc -O -o main main.swift
$ ldd main
linux-vdso.so.1 (0x00007ffdcfcca000)
libswiftCore.so => /mnt/scratch/spse/builddir/swift-linux-x86_64/lib/swift/linux/x86_64/libswiftCore.so (0x00007fe776a38000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fe77672d000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fe77642c000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fe776216000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe775e6b000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fe775c4e000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fe775a4a000)
libicuuc.so.52 => /usr/lib/x86_64-linux-gnu/libicuuc.so.52 (0x00007fe7756cc000)
libicui18n.so.52 => /usr/lib/x86_64-linux-gnu/libicui18n.so.52 (0x00007fe7752ba000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe776e61000)
libicudata.so.52 => /usr/lib/x86_64-linux-gnu/libicudata.so.52 (0x00007fe773a4d000)
$ ./main
sectionName: .swift2_protocol_conformances_start sectionDataAddr: 0x7f16a16867f0 *sectionDataAddr: (nil)
Dynamic binary, using dl_iterate_phdr
hello
# Dynamic binary with libswiftCore.a linked in
$ swiftc -O -static-stdlib -o main main.swift
$ ldd main
linux-vdso.so.1 (0x00007ffeffb1a000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f68dfe62000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f68dfc45000)
libbsd.so.0 => /lib/x86_64-linux-gnu/libbsd.so.0 (0x00007f68dfa35000)
libicui18n.so.52 => /usr/lib/x86_64-linux-gnu/libicui18n.so.52 (0x00007f68df623000)
libicuuc.so.52 => /usr/lib/x86_64-linux-gnu/libicuuc.so.52 (0x00007f68df2a5000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f68def9a000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f68dec99000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f68dea83000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f68de6d8000)
/lib64/ld-linux-x86-64.so.2 (0x00007f68e0066000)
libicudata.so.52 => /usr/lib/x86_64-linux-gnu/libicudata.so.52 (0x00007f68dce6b000)
$ ./main
sectionName: .swift2_protocol_conformances_start sectionDataAddr: 0x72d790 *sectionDataAddr: (nil)
Dynamic binary, using dl_iterate_phdr
hello
# Static binary
$ swiftc -O -emit-object -o main.o main.swift
$ clang -Wall -O3 -c static_stub.c -o static_stub.o
$ clang++ -g -static -fuse-ld=gold -target x86_64-unknown-linux-gnu $LIBDIR/swift/linux/x86_64/swift_begin.o main.o -L $LIBDIR/swift_static/linux -Xlinker -export-dynamic -Xlinker --exclude-libs -Xlinker ALL -Xlinker --no-undefined --target=x86_64-unknown-linux-gnu @main.autolink $LIBDIR/swift/linux/x86_64/swift_end.o -o main
/mnt/scratch/spse/builddir/swift-linux-x86_64/lib/swift_static/linux/libswiftCore.a(ProtocolConformance.cpp.o):/mnt/hgfs/src/swift/swift/stdlib/public/runtime/ProtocolConformance.cpp:function _addImageProtocolConformances(dl_phdr_info*, unsigned long, void*): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/libicuuc.a(putil.ao):function uprv_dl_open_52: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
$ ldd main
not a dynamic executable
$ ./main
sectionName: .swift2_protocol_conformances_start sectionDataAddr: 0x1fb1588 *sectionDataAddr: 0x44f0
Static binary Adding blockAddr: 0x1fb1590, blockSize: 17648
hello
$
- Simon