Crystal: Static link using multi-thread fails

Created on 1 Feb 2020  路  8Comments  路  Source: crystal-lang/crystal

Congrats supporting multithreading!
It works fine, but binary creation seems to fail with multithreaded and statically linked.

minimum code

Anything.

p 1

reproduce

$ docker run -it -u `id -u` -v `pwd`:/mnt -w /mnt --rm crystallang/crystal:0.32.1 bash

$ crystal build test.cr --link-flags "-static" -Dpreview_mt
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libevent.a(evutil.o): In function `test_for_getaddrinfo_hacks':
(.text+0x2e4): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libevent.a(evutil.o): In function `evutil_getaddrinfo_common_':
(.text+0x1beb): warning: Using 'getprotobynumber' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
(.text+0x1cc3): warning: Using 'getservbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libevent_pthreads.a(evthread_pthread.o): In function `evthread_posix_cond_free':
(.text+0x55): undefined reference to `pthread_cond_destroy'
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libevent_pthreads.a(evthread_pthread.o): In function `evthread_posix_cond_alloc':
(.text+0x89): undefined reference to `pthread_cond_init'
collect2: error: ld returned 1 exit status
Error: execution of command failed with code: 1: `cc "${@}" -o '/mnt/test' -static -rdynamic  -lpcre -lm /usr/bin/../lib/crystal/lib/libgc.a -lpthread /usr/share/crystal/src/ext/libcrystal.a -levent_pthreads -levent -lrt -ldl -L/usr/bin/../lib/crystal/lib -L/usr/lib -L/usr/local/lib`

It works fine in following cases.

$ crystal build test.cr -Dpreview_mt             # only multithreaded
$ crystal build test.cr --link-flags "-static"   # only statically linked
$ crystal --version
Crystal 0.32.1 [41bd18fbe] (2019-12-18)

LLVM: 8.0.0
Default target: x86_64-unknown-linux-gnu

Best regards,

Most helpful comment

@straight-shoota yes:

diff --git a/src/crystal/system/unix/lib_event2.cr b/src/crystal/system/unix/lib_event2.cr
index f898c528b..7d723754e 100644
--- a/src/crystal/system/unix/lib_event2.cr
+++ b/src/crystal/system/unix/lib_event2.cr
@@ -11,6 +11,7 @@ require "c/netdb"
   @[Link("event")]
 {% end %}
 {% if flag?(:preview_mt) %}
+  @[Link("pthread")]
   @[Link("event_pthreads")]
 {% end %}
 lib LibEvent2

Weirdly, I must declare @[Link("pthread")] before @[Link("event_pthreads")] for the -lpthread to be inserted after -levent_pthreads ?!

All 8 comments

Not really related to MT. This is glibc warning that despite being compiled statically it still still dynamically load some libraries at runtime (sigh).

@ysbaddaden
Yeah, I know it. I put the log as it was, but I had to cut off the first half. 馃槄
The essential problem is this part of the second half. Compilation was not successful.

undefined reference to `pthread_cond_destroy'
undefined reference to `pthread_cond_init'
collect2: error: ld returned 1 exit status

Oh, sorry! I reproduced. This is a CC or LD trickery.

It fails to compile when -lpthread is _before_ -levent_pthreads. If we move it after, or declare it a second time, like -lpthread ... -levent_pthreads -lpthread then it compiles fine...

@ysbaddaden
worked! You have prevented me from wasting time. Thank you very much. 馃挴 馃槏

$ crystal build test.cr --link-flags "-static -levent_pthreads -lpthread" -Dpreview_mt
$ ldd test
        not a dynamic executable
$ ./test
1

Can we make this work without manual configuration?

@straight-shoota yes:

diff --git a/src/crystal/system/unix/lib_event2.cr b/src/crystal/system/unix/lib_event2.cr
index f898c528b..7d723754e 100644
--- a/src/crystal/system/unix/lib_event2.cr
+++ b/src/crystal/system/unix/lib_event2.cr
@@ -11,6 +11,7 @@ require "c/netdb"
   @[Link("event")]
 {% end %}
 {% if flag?(:preview_mt) %}
+  @[Link("pthread")]
   @[Link("event_pthreads")]
 {% end %}
 lib LibEvent2

Weirdly, I must declare @[Link("pthread")] before @[Link("event_pthreads")] for the -lpthread to be inserted after -levent_pthreads ?!

We do an explicit reverse in the compiler because the order you have to pass these to the linker seems counter-intuitive:

https://github.com/crystal-lang/crystal/blob/ec26d82a57721c89e1cc0d21052728cda1fd7e85/src/compiler/crystal/codegen/link.cr#L112

I can't remember how it is exactly... event_pthreads uses pthread so it would make sense for pthread to come before event_pthreads in the linker command. However, if I remember correctly, if you include a lib and no previous lib needed it, it's discarded or something like that.

We could probably revisit this ordering decision...

Weirdly, I must declare @[Link("pthread")] before @[Link("event_pthreads")] for the -lpthread to be inserted after -levent_pthreads ?!

-levent_pthreads depends on -lpthread so the order in the sourcecode is correct.

Please submit a PR with this patch @ysbaddaden!

Was this page helpful?
0 / 5 - 0 ratings