Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build a statically linked Haskell library with ghc-musl #28

Open
Abhiroop opened this issue Nov 3, 2022 · 5 comments
Open

Build a statically linked Haskell library with ghc-musl #28

Abhiroop opened this issue Nov 3, 2022 · 5 comments

Comments

@Abhiroop
Copy link

Abhiroop commented Nov 3, 2022

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?

@Abhiroop
Copy link
Author

Abhiroop commented Nov 4, 2022

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

@TravisCardwell
Copy link
Collaborator

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.

@Abhiroop
Copy link
Author

Abhiroop commented Nov 4, 2022

@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?

@TravisCardwell
Copy link
Collaborator

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.

@benz0li
Copy link
Contributor

benz0li commented Aug 9, 2023

@Abhiroop See also commercialhaskell/stack#3420

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants