Skip to content

Can't build statically linked executable when Template Haskell is used

Summary

I want to build a statically linked executable using musl libc. As I know, the suggested way of doing it is to invoke ghc with -optl=-pthread -optl=-static flags, which make it pass -pthread -static flags to the linker. But it fails when my code contains a TH splice, with the following error message:

$ x86_64-unknown-linux-musl-ghc -optl=-static -optl=-pthread Main.hs
<...>
x86_64-unknown-linux-musl-ld: crtbeginT.o: relocation R_X86_64_32 against hidden symbol `__TMC_END__' can not be used when making a shared object
x86_64-unknown-linux-musl-ld: 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

Invoking ghc with -v flag shows the linker invocation it fails on:

x86_64-unknown-linux-musl-cc <...> -o /run/user/1000/ghc380334_0/libghc_6.so ./Foo.dyn_o -shared <...> -lgmp -lc -lm -static -pthread

What makes the linker fail are -shared and -static flags used at the same time. I found out that the linker is invoked not only for linking the final executable, but also for producing .so libraries when TH code is processed. And -static flag is passed in both cases, while we only want to pass it in the first one. Is there a way to achieve that?

Steps to reproduce

(Tarball with these files: ghc-static-issue.tar.gz, the example is originally due to Joachim Breitner: https://github.com/input-output-hk/haskell.nix/issues/914#issuecomment-724625620.)

Main.hs:

{-# LANGUAGE TemplateHaskell #-}
module Main where

import Foo

main :: IO ()
main = print $(Foo.simpleVersion "foo")

Foo.hs:

{-# LANGUAGE TemplateHaskell #-}
module Foo where

import           Language.Haskell.TH (Q,Exp)
import qualified Language.Haskell.TH.Syntax as TH

simpleVersion :: String -> Q Exp
simpleVersion version = [|$(TH.lift version)|]

shell.nix with GHC linked against musl:

let
  nixpkgs-src = builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/2030abed5863fc11eccac0735f27a0828376c84e.tar.gz";
  pkgs = (import nixpkgs-src {}).pkgsCross.musl64;
in pkgs.stdenv.mkDerivation {
  name = "env";
  # ghc 8.10.4 executable, dynamically linked with musl:
  nativeBuildInputs = [ pkgs.ghc ];
  # provide both .a and .so libraries for gmp and libffi:
  buildInputs = [
    (pkgs.gmp.override { withStatic = true; })
    (pkgs.libffi.overrideAttrs (_old: { dontDisableStatic = true; }))
  ];
}

To reproduce run:

$ nix-shell --pure shell.nix
$ x86_64-unknown-linux-musl-ghc -optl=-static -optl=-pthread -v Main.hs

Expected behavior

A statically linked executable compiled.

Environment

  • GHC version used: 8.10.4, dynamically linked against musl
  • Operating System: Linux
  • System Architecture: x86_64
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information