Adding template Haskell library produces linker error on Alpine

I have an almost pure Haskell project that I compile to a static executable using an Alpine Docker base image and a build script that installs all of Haskell’s and ghcups Alpine dependencies and then installs ghcup and basically does cabal build. So far, so good. It works!

However, once I add the microlens-th dependency and actually use it, for instance in this dummy snippet in Main:

data AppState = State1 {_state11 :: Int, _state12 :: Int}

makeLenses ''AppState

I get this linker error:

167.0 <no location info>: error:
167.0     Error relocating /root/.cabal/store/ghc-9.8.4-04ad/hs-asapo-0.9.1-75d4d1bac2189d3038e18192ad5d3761b640dba0a457200cd21dbe936e615a99/lib/libHShs-asapo-0.9.1-75d4d1bac2189d3038e18192ad5d3761b640dba0a457200cd21dbe936e615a99-ghc9.8.4.so: _ZSt17__throw_bad_allocv: symbol not found

I’ve talked to the author of microlens-th and we’re in agreement that it’s probably not a microlens-th issue, because there is nothing special about it.

But what could cause this weird error then? It happens both with ghc-9.6 and ghc-9.8. I haven’t tried newer ones yet.

5 Likes

This sounds like a GHC/linker bug. Could you report it on the GHC tracker?

1 Like

I’m not sure if it’s related, but your ld-options is a hack.

I tried to fix it, but I ran into cabal being broken in various ways: pkg-config: Use --libs --static when building with using --enable-executable-static by alexbiehl · Pull Request #6935 · haskell/cabal · GitHub (the pkg-config file of asapo is broken too, because it doesn’t make correct use of Requires.private).

So yes, it’s possible that everything is broken.

3 Likes

Oh yes, these ld-options come straight from hell. But at some point I was frustrated and wanted something that at least links sometimes.

Thanks for digging deeper into this. I can definitely fix the pkg-config problem in asapo, since I am one of the developers, and I’ll keep an eye on this GitHub issue.

Just an update. It turned out it’s also a GHC issue: #26434: Support statically linking executables properly · Issues · Glasgow Haskell Compiler / GHC · GitLab

libcurl needs to go into Requires.private. That makes pkg-config expand static library dependencies recursively.

1 Like

See also this issue I created.

I may have hit something related, though I’m not trying to statically link. I’m upgrading a project from 9.6.7 to 9.8.4, where we have something like

library
  import: common-options
  build-depends: async, stm
  exposed-modules: Lib

executable myexe
  import: common-options
  build-depends:      myproject, optparse-applicative
  main-is:            Main.hs
  extra-lib-dirs:     lib
  extra-libraries:    odbc
  ghc-options:        -threaded -rtsopts -with-rtsopts=-T

test-suite quickcheck
  import: common-options
  build-depends:      myproject, QuickCheck
  main-is:            Spec.hs

and Spec.hs uses Template Haskell for quickcheck’s $forAllProperties. This all worked fine on 9.6.7 and non-fast builds.

But with 9.8.4, the executable builds, links and executes fine, but the test suite fails with undefined symbol: SQLPrepare. But if I put the whole test suite code into a module in the main project library, and just call that from Spec.hs – everything works again.


Things that did not work:

It does not help to put extra-lib-dirs/extra-libraries into test quickcheck.

It does not matter whether it says test-suite or executable.

It also does not work to have a second library for tests like

library qclib
  import: common-options
  build-depends:     myproject, QuickCheck
  hs-source-dirs:     test
  exposed-modules:      QCInstances, SpecLib

test-suite quickcheck
  import: common-options
  build-depends:      qclib

(the above fails with undefined symbol on building qclib, before it even tries to build the test-suite).


EDIT: I can actually get the error on 9.6.7 too, if I do stack clean && stack test --fast myproject:test:quickcheck

If I put libcurl in Requires.private, this will put all the libraries libcurl needs into the command line, so I end up with -lpsl -lnghttp3 and such. However, I have both a .so and a .a version of these libraries installed, so I was hoping by some cabal magic it would choose the .a one and link just fine, but that does not seem to be the case. I still get linker errors. Or maybe it’s the cabal issue you linked that causes this. Not sure.

Yes, because of the issues I linked above.

GHC is responsible for collecting link options for dependencies of the package you’re currently building (cabal just tells GHC which packages are dependencies). But GHC only uses extra-libraries and ignores extra-libraries-static field of the package configuration files… but this is exactly where cabal will insert pkg-config --libs --static asapo.

So GHC needs to be made aware of that.

So when GHC links statically, only pkg-config --libs asapo ends up being considered.

1 Like