Wrong order of `-lc` and `-lpthread` when building a static binary
Summary
(Let’s ignore for a second all the downsides of static linking in general and with glibc in particular.)
I am trying to link a trivial Haskell program fully statically on Linux with GHC 8.8.4:
$ ghc -static -optl-static main.hs -o main
Linking main ...
/usr/bin/ld.gold: error: /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/libpthread.a(lowlevellock.o): multiple definition of '__lll_lock_wait_private'
/usr/bin/ld.gold: /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/libc.a(libc-lowlevellock.o): previous definition here
/usr/bin/ld.gold: error: /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/libpthread.a(lowlevellock.o): multiple definition of '__lll_unlock_wake_private'
/usr/bin/ld.gold: /usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/libc.a(libc-lowlevellock.o): previous definition here
collect2: error: ld returned 1 exit status
`gcc' failed in phase `Linker'. (Exit code: 1)
(Note: the line above works fine with GHC 8.4.4.)
When compiling with -v
I see:
*** Linker:
gcc -DTABLES_NEXT_TO_CODE '-fuse-ld=gold' -Wl,--no-as-needed -static -o main -lm -no-pie -fno-PIC -Wl,--gc-sections main.o -L/opt/ghc/8.8.4/lib/ghc-8.8.4/base-4.13.0.0 -L/opt/ghc/8.8.4/lib/ghc-8.8.4/integer-gmp-1.0.2.0 -L/opt/ghc/8.8.4/lib/ghc-8.8.4/ghc-prim-0.5.3 -L/opt/ghc/8.8.4/lib/ghc-8.8.4/rts /tmp/ghc1_0/ghc_2.o /tmp/ghc1_0/ghc_5.o -Wl,-u,base_GHCziTopHandler_runIO_closure -Wl,-u,base_GHCziTopHandler_runNonIO_closure -Wl,-u,ghczmprim_GHCziTuple_Z0T_closure -Wl,-u,ghczmprim_GHCziTypes_True_closure -Wl,-u,ghczmprim_GHCziTypes_False_closure -Wl,-u,base_GHCziPack_unpackCString_closure -Wl,-u,base_GHCziWeak_runFinalizzerBatch_closure -Wl,-u,base_GHCziIOziException_stackOverflow_closure -Wl,-u,base_GHCziIOziException_heapOverflow_closure -Wl,-u,base_GHCziIOziException_allocationLimitExceeded_closure -Wl,-u,base_GHCziIOziException_blockedIndefinitelyOnMVar_closure -Wl,-u,base_GHCziIOziException_blockedIndefinitelyOnSTM_closure -Wl,-u,base_GHCziIOziException_cannotCompactFunction_closure -Wl,-u,base_GHCziIOziException_cannotCompactPinned_closure -Wl,-u,base_GHCziIOziException_cannotCompactMutable_closure -Wl,-u,base_ControlziExceptionziBase_absentSumFieldError_closure -Wl,-u,base_ControlziExceptionziBase_nonTermination_closure -Wl,-u,base_ControlziExceptionziBase_nestedAtomically_closure -Wl,-u,base_GHCziEventziThread_blockedOnBadFD_closure -Wl,-u,base_GHCziConcziSync_runSparks_closure -Wl,-u,base_GHCziConcziIO_ensureIOManagerIsRunning_closure -Wl,-u,base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure -Wl,-u,base_GHCziConcziSignal_runHandlersPtr_closure -Wl,-u,base_GHCziTopHandler_flushStdHandles_closure -Wl,-u,base_GHCziTopHandler_runMainIO_closure -Wl,-u,ghczmprim_GHCziTypes_Czh_con_info -Wl,-u,ghczmprim_GHCziTypes_Izh_con_info -Wl,-u,ghczmprim_GHCziTypes_Fzh_con_info -Wl,-u,ghczmprim_GHCziTypes_Dzh_con_info -Wl,-u,ghczmprim_GHCziTypes_Wzh_con_info -Wl,-u,base_GHCziPtr_Ptr_con_info -Wl,-u,base_GHCziPtr_FunPtr_con_info -Wl,-u,base_GHCziInt_I8zh_con_info -Wl,-u,base_GHCziInt_I16zh_con_info -Wl,-u,base_GHCziInt_I32zh_con_info -Wl,-u,base_GHCziInt_I64zh_con_info -Wl,-u,base_GHCziWord_W8zh_con_info -Wl,-u,base_GHCziWord_W16zh_con_info -Wl,-u,base_GHCziWord_W32zh_con_info -Wl,-u,base_GHCziWord_W64zh_con_info -Wl,-u,base_GHCziStable_StablePtr_con_info -Wl,-u,hs_atomic_add8 -Wl,-u,hs_atomic_add16 -Wl,-u,hs_atomic_add32 -Wl,-u,hs_atomic_add64 -Wl,-u,hs_atomic_sub8 -Wl,-u,hs_atomic_sub16 -Wl,-u,hs_atomic_sub32 -Wl,-u,hs_atomic_sub64 -Wl,-u,hs_atomic_and8 -Wl,-u,hs_atomic_and16 -Wl,-u,hs_atomic_and32 -Wl,-u,hs_atomic_and64 -Wl,-u,hs_atomic_nand8 -Wl,-u,hs_atomic_nand16 -Wl,-u,hs_atomic_nand32 -Wl,-u,hs_atomic_nand64 -Wl,-u,hs_atomic_or8 -Wl,-u,hs_atomic_or16 -Wl,-u,hs_atomic_or32 -Wl,-u,hs_atomic_or64 -Wl,-u,hs_atomic_xor8 -Wl,-u,hs_atomic_xor16 -Wl,-u,hs_atomic_xor32 -Wl,-u,hs_atomic_xor64 -Wl,-u,hs_cmpxchg8 -Wl,-u,hs_cmpxchg16 -Wl,-u,hs_cmpxchg32 -Wl,-u,hs_cmpxchg64 -Wl,-u,hs_atomicread8 -Wl,-u,hs_atomicread16 -Wl,-u,hs_atomicread32 -Wl,-u,hs_atomicread64 -Wl,-u,hs_atomicwrite8 -Wl,-u,hs_atomicwrite16 -Wl,-u,hs_atomicwrite32 -Wl,-u,hs_atomicwrite64 -lHSbase-4.13.0.0 -lHSinteger-gmp-1.0.2.0 -lHSghc-prim-0.5.3 -lHSrts -lCffi -lgmp -lc -lm -lm -lrt -ldl -lpthread
Note that at the end of the line -pthread
follows -lc
. There is a well-known issue with statically linking both glibc
and pthread
: the latter should be earlier on the command line. I can reproduce it with the following C program:
#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int main()
{
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
}
Compiling it with gcc -fuse-ld=gold -static main.c -lc -lpthread -o main
results in:
/usr/bin/ld.gold: error: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libpthread.a(lowlevellock.o): multiple definition of '__lll_lock_wait_private'
/usr/bin/ld.gold: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(libc-lowlevellock.o): previous definition here
/usr/bin/ld.gold: error: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libpthread.a(lowlevellock.o): multiple definition of '__lll_unlock_wake_private'
/usr/bin/ld.gold: /usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(libc-lowlevellock.o): previous definition here
collect2: error: ld returned 1 exit status
while compiling with gcc -fuse-ld=gold -static main.c -lpthread -lc -o main
succeeds. Omitting -lc
entirely works too.
The same linker command line with -v
and GHC 8.4.4 is:
gcc -fno-stack-protector -DTABLES_NEXT_TO_CODE '-fuse-ld=gold' -Wl,--no-as-needed -static -o main -lm -no-pie -Wl,--gc-sections main.o -L/opt/ghc/8.4.4/lib/ghc-8.4.4/base-4.11.1.0 -L/opt/ghc/8.4.4/lib/ghc-8.4.4/integer-gmp-1.0.2.0 -L/opt/ghc/8.4.4/lib/ghc-8.4.4/ghc-prim-0.5.2.0 -L/opt/ghc/8.4.4/lib/ghc-8.4.4/rts /tmp/ghc1_0/ghc_5.o /tmp/ghc1_0/ghc_8.o -Wl,-u,base_GHCziTopHandler_runIO_closure -Wl,-u,base_GHCziTopHandler_runNonIO_closure -Wl,-u,ghczmprim_GHCziTuple_Z0T_closure -Wl,-u,ghczmprim_GHCziTypes_True_closure -Wl,-u,ghczmprim_GHCziTypes_False_closure -Wl,-u,base_GHCziPack_unpackCString_closure -Wl,-u,base_GHCziWeak_runFinalizzerBatch_closure -Wl,-u,base_GHCziIOziException_stackOverflow_closure -Wl,-u,base_GHCziIOziException_heapOverflow_closure -Wl,-u,base_GHCziIOziException_allocationLimitExceeded_closure -Wl,-u,base_GHCziIOziException_blockedIndefinitelyOnMVar_closure -Wl,-u,base_GHCziIOziException_blockedIndefinitelyOnSTM_closure -Wl,-u,base_GHCziIOziException_cannotCompactFunction_closure -Wl,-u,base_GHCziIOziException_cannotCompactPinned_closure -Wl,-u,base_GHCziIOziException_cannotCompactMutable_closure -Wl,-u,base_ControlziExceptionziBase_absentSumFieldError_closure -Wl,-u,base_ControlziExceptionziBase_nonTermination_closure -Wl,-u,base_ControlziExceptionziBase_nestedAtomically_closure -Wl,-u,base_GHCziEventziThread_blockedOnBadFD_closure -Wl,-u,base_GHCziConcziSync_runSparks_closure -Wl,-u,base_GHCziConcziIO_ensureIOManagerIsRunning_closure -Wl,-u,base_GHCziConcziIO_ioManagerCapabilitiesChanged_closure -Wl,-u,base_GHCziConcziSignal_runHandlersPtr_closure -Wl,-u,base_GHCziTopHandler_flushStdHandles_closure -Wl,-u,base_GHCziTopHandler_runMainIO_closure -Wl,-u,ghczmprim_GHCziTypes_Czh_con_info -Wl,-u,ghczmprim_GHCziTypes_Izh_con_info -Wl,-u,ghczmprim_GHCziTypes_Fzh_con_info -Wl,-u,ghczmprim_GHCziTypes_Dzh_con_info -Wl,-u,ghczmprim_GHCziTypes_Wzh_con_info -Wl,-u,base_GHCziPtr_Ptr_con_info -Wl,-u,base_GHCziPtr_FunPtr_con_info -Wl,-u,base_GHCziInt_I8zh_con_info -Wl,-u,base_GHCziInt_I16zh_con_info -Wl,-u,base_GHCziInt_I32zh_con_info -Wl,-u,base_GHCziInt_I64zh_con_info -Wl,-u,base_GHCziWord_W8zh_con_info -Wl,-u,base_GHCziWord_W16zh_con_info -Wl,-u,base_GHCziWord_W32zh_con_info -Wl,-u,base_GHCziWord_W64zh_con_info -Wl,-u,base_GHCziStable_StablePtr_con_info -Wl,-u,hs_atomic_add8 -Wl,-u,hs_atomic_add16 -Wl,-u,hs_atomic_add32 -Wl,-u,hs_atomic_add64 -Wl,-u,hs_atomic_sub8 -Wl,-u,hs_atomic_sub16 -Wl,-u,hs_atomic_sub32 -Wl,-u,hs_atomic_sub64 -Wl,-u,hs_atomic_and8 -Wl,-u,hs_atomic_and16 -Wl,-u,hs_atomic_and32 -Wl,-u,hs_atomic_and64 -Wl,-u,hs_atomic_nand8 -Wl,-u,hs_atomic_nand16 -Wl,-u,hs_atomic_nand32 -Wl,-u,hs_atomic_nand64 -Wl,-u,hs_atomic_or8 -Wl,-u,hs_atomic_or16 -Wl,-u,hs_atomic_or32 -Wl,-u,hs_atomic_or64 -Wl,-u,hs_atomic_xor8 -Wl,-u,hs_atomic_xor16 -Wl,-u,hs_atomic_xor32 -Wl,-u,hs_atomic_xor64 -Wl,-u,hs_cmpxchg8 -Wl,-u,hs_cmpxchg16 -Wl,-u,hs_cmpxchg32 -Wl,-u,hs_cmpxchg64 -Wl,-u,hs_atomicread8 -Wl,-u,hs_atomicread16 -Wl,-u,hs_atomicread32 -Wl,-u,hs_atomicread64 -Wl,-u,hs_atomicwrite8 -Wl,-u,hs_atomicwrite16 -Wl,-u,hs_atomicwrite32 -Wl,-u,hs_atomicwrite64 -lHSbase-4.11.1.0 -lHSinteger-gmp-1.0.2.0 -lHSghc-prim-0.5.2.0 -lHSrts -lCffi -lgmp -lm -lrt -ldl -lpthread
Note that -lc
is missing, so, my theory is that is what changed in 8.8. However, just FTR, I am not entirely sure that my analysis is completely right.
Steps to reproduce
echo "main = pure ()" > main.hs
-
ghc -static -optl-static main.hs -o main
- or
docker run --rm -v $(pwd):/sandbox -w /sandbox haskell:8.8.4 ghc -static -optl-static main.hs -o main
- or
Expected behavior
It should compile without any errors.
Environment
- GHC version used: 8.8.4
Optional:
- Operating System: Linux
- System Architecture: