timds
timds

Reputation: 43

How do I compile a Haskell Mach-O executable that exports a symbol?

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 exports 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 defaultld, 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

Answers (1)

HTNW
HTNW

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

Related Questions