ghc-musl
ghc-musl copied to clipboard
Build a statically linked Haskell library with ghc-musl
Hello,
I am attempting to statically link a Haskell library that exports a C API. But I get the following errors if I pass the -optl-static
flag:
/usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../x86_64-alpine-linux-musl/bin/ld: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/crtbeginT.o: relocation R_X86_64_32 against hidden symbol `__TMC_END__' can not be used when making a shared object
/usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../x86_64-alpine-linux-musl/bin/ld: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/crtend.o: relocation R_X86_64_32 against `.ctors' can not be used when making a shared object; recompile with -fPIC
collect2: error: ld returned 1 exit status
without the -optl-static
flag the file is linked but when I run ldd
I see there are dependencies like the following:
ldd libEval.so
/lib/ld-musl-x86_64.so.1 (0x7f74a28ec000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f74a28ec000)
libgmp.so.10 => /usr/lib/libgmp.so.10 (0x7f74a1d16000)
Error relocating libEval.so: __fini_array_start: symbol not found
Error relocating libEval.so: __fini_array_end: symbol not found
Let me share the full set of commands and the full program so that the error can be reproduced. Here is what the programs looks like (taken from http://www.vex.net/~trebla/haskell/so.xhtml#top):
Eval.hs
module Eval() where
import Prelude hiding (lex)
import Foreign.C
import Foreign
import Control.Applicative
import Text.ParserCombinators.ReadP
import Text.Read.Lex
foreign export ccall "eval" c_eval :: CString -> Ptr CInt -> IO (Ptr CInt)
c_eval s r = do
cs <- peekCAString s
case hs_eval cs of
Nothing -> return nullPtr
Just x -> do
poke r x
return r
hs_eval :: String -> Maybe CInt
hs_eval inp = case readP_to_S expr inp of
(a,_) : _ -> Just a
[] -> Nothing
expr = addition <* expect EOF
addition = chainl1 multiplication add
where
add = expect (Symbol "+") >> return (+)
multiplication = chainl1 atom mul
where
mul = expect (Symbol "*") >> return (*)
atom = number <|> between lp rp addition
number = do
Number n <- lex
case numberToInteger n of
Just i -> return (fromIntegral i)
Nothing -> pfail
lp = expect (Punc "(")
rp = expect (Punc ")")
hsbracket.c
#include <HsFFI.h>
static void my_enter(void) __attribute__((constructor));
static void my_enter(void)
{
static char *argv[] = { "libEval.so", 0 }, **argv_ = argv;
static int argc = 1;
hs_init(&argc, &argv_);
}
static void my_exit(void) __attribute__((destructor));
static void my_exit(void)
{
hs_exit();
}
The command that I run inside your docker image is this:
ghc -O2 -fPIC -c hsbracket.c
ghc -O2 -shared -flink-rts -fPIC -o libEval.so Eval.hs hsbracket.o
This one successfully produces the shared library, but if I do ldd I get:
/lib/ld-musl-x86_64.so.1 (0x7facb50a6000)
libgmp.so.10 => /usr/lib/libgmp.so.10 (0x7facb44d0000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7facb50a6000)
Error relocating libEval.so: __fini_array_start: symbol not found
Error relocating libEval.so: __fini_array_end: symbol not found
Another set of commands that I tried with the above program is
ghc -O2 -fPIC -c hsbracket.c
ghc -O2 -shared -flink-rts -optl-static -fPIC -o libEval.so Eval.hs hsbracket.o
But this fails with
Linking libEval.so ...
/usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../x86_64-alpine-linux-musl/bin/ld: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/crtbeginT.o: relocation R_X86_64_32 against hidden symbol `__TMC_END__' can not be used when making a shared object
/usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/../../../../x86_64-alpine-linux-musl/bin/ld: /usr/lib/gcc/x86_64-alpine-linux-musl/10.3.1/crtend.o: relocation R_X86_64_32 against `.ctors' can not be used when making a shared object; recompile with -fPIC
collect2: error: ld returned 1 exit status
`gcc' failed in phase `Linker'. (Exit code: 1)
Is it not possible to statically link this library?
Now when I do ghc -O2 -optl-fuse-ld=gold -optl-static -shared -flink-rts -fPIC -o libEval.so Eval.hs hsbracket.o
(not the use of the gold
linker) I get
ldd libEval.so
/lib/ld-musl-x86_64.so.1 (0x7fabb8e96000)
libgmp.so.10 => /usr/lib/libgmp.so.10 (0x7fabb82be000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7fabb8e96000)
wonder is it not possible to link the remaining 3 libraries as well.
EDIT I think I understand that the first and third dependencies are the libc interpreter
Hi!
I have not built static libraries before. I tried your example and get the same results. I think that your second set of commands is the most promising, and the error message seems to match the following documentation in the Shared libraries that export a C API section of the GHC User's Guide:
In principle you can use
-shared
without-dynamic
in the link step. That means to statically link the runtime system and all of the base libraries into your new shared library. This would make a very big, but standalone shared library. On most platforms however that would require all the static libraries to have been built with-fPIC
so that the code is suitable to include into a shared library and we do not do that at the moment.
Perhaps it would work if crtbeginT.o
and crtend.o
were built with using PIC... I built a version of GCC with PIC, and it resolved the second error but not the first:
Linking libEval.so ...
/usr/bin/ld: /root/ghc-11.2.0/lib/gcc/x86_64-pc-linux-musl/11.2.0/crtbeginT.o: relocation R_X86_64_32 against hidden symbol `__TMC_END__' can not be used when making a shared object
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
`gcc' failed in phase `Linker'. (Exit code: 1)
I am out of time today, but perhaps I will get a chance to try again this weekend.
@TravisCardwell Thanks for looking into it.
One thing I noticed is that when building GHC if it is configured with --with-intree-gmp
then libgmp gets statically linked. I wonder if the ghc-musl binary here is configured with this flag.
Also, do you have any directives or instructions on how to compile ghc with musl?
We are using GHCUp to install an official binary package. There is likely a script somewhere that specifies exactly how the package is configured and built, but I have not been able to find one. Unfortunately, ghc --info
does not provide the this information.
There are two official binary packages for Alpine (example: GHC 9.4.3): one using the GMP bignum implementation that we are using, and another using the Haskell-native bignum implementation. It might we worthwhile to try out the Haskell-native bignum implementation.
I do not have any special instructions for building GHC on Alpine. I would start by following the instructions in the hadrian
directory and Hadrian wiki page.
@Abhiroop See also https://github.com/commercialhaskell/stack/issues/3420