Reputation: 43
How do I compile a Haskell executable (not a library) using GHC >=8.2.1 (preferably on 8.6.*) on macOS 10.14.5 that successfully foreign export
s a function (i.e. exposes it as a public symbol in the binary)? This works in GHC 8.0.2 but not in later versions.
I've tried this on ghc 8.0.2 (via stack), 8.2.2 (via both nix and stack), 8.4.4 (via both nix and stack), and 8.6.4 (via both nix and stack). Only 8.0.2 works. I know that 8.2 introduced this change: GHC will now use ld.gold or ld.lld instead of the system’s default
ld, if available.
But I am unclear whether 8.0.2 would use a different 'system default' ld in the first place (darwin's ld as opposed to llvm's lld?). I know of ghc's -pgml [ld-program-here]
option to set the linker but I haven't been able to manually set it to anything that has successfully compiled and linked anything to test further.
I have this file Main.hs
:
module Main where
import Foreign.C
foreign export ccall "my_square" my_square :: CInt -> CInt
my_square :: CInt -> CInt
my_square x = x * x
main :: IO ()
main = putStrLn "FFI Test"
With ghc 8.0.2:
> rm Main.hi Main.o && ghc-8.0.2 Main.hs && nm -g Main | grep my_square
[1 of 1] Compiling Main ( Main.hs, Main.o )
[some apparently unrelated clang warnings]
Linking Main ...
[further clang warnings]
2544:0000000100001260 T _my_square
With ghc 8.6.4, the symbol doesn't seem to be exported, so future attempts to link another program with this symbol fail.
> rm Main.hi Main.o && ghc-8.6.4 Main.hs && nm -g Main | grep my_square
[1 of 1] Compiling Main ( Main.hs, Main.o )
Linking Main ...
Upvotes: 4
Views: 157
Reputation: 29193
Going off what I said in my comment, neither version of GHC you tested actually exports _my_square
from Main
. Instead, they export _my_square
from Main.o
(this is why you can use ghc
to compile and link a .c
file that uses my_square
). When Main.o
is linked into the final Main
executable, neither GHC tells the linker to export _my_square
. GHC 8.6.4 (and, I think, since 8.2) tells the linker to strip unused, unexported symbols, so _my_square
evaporates. GHC 8.0.2 does not, leaving _my_sqaure
in the Main
executable purely by accident.
You can tell GHC to tell the linker to keep the symbol. The linker option for this is -exported_symbol <symbol>
. My GHC actually invokes the linker through gcc
, so I have to wrap those two arguments with -Wl
. For whatever reason, passing that option to the GHC actually compiling Main.hs
causes an error; you have to make Main.o
with one bare GHC invocation and then call GHC again to link it into Main
, this time with the linker options. Maybe this is peculiar to my system—you may experiment to find a better way.
$ ghc -c Main.hs
# Makes Main.hi Main.o Main_stub.h
$ ghc -optl-Wl,-exported_symbol -optl-Wl,_my_square Main.o -o Main
# Makes Main
$ objdump -t Main | grep _my_square
0000000100000dc0 g F __TEXT,__text _my_square
I would still recommend filing a GHC issue to make this easier.
Upvotes: 1