Building fully static binaries on Linux


(Simon Evans) #1

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()
@ https://gist.github.com/spevans/17d01a687c0648a8adc5021029b6d648

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