I am familiar with the approach of calling a Haskell function from C as defined here:
https://wiki.haskell.org/Calling_Haskell_from_C
However, instead of creating an executable, what I want to do is create a .o
that will be later linked with other .o
files. My scenario is something like this:
Safe.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Safe where
import Foreign.C.Types
fibonacci :: Int -> Int
fibonacci n = fibs !! n
where fibs = 0 : 1 : zipWith ( ) fibs (tail fibs)
fibonacci_hs :: CInt -> CInt
fibonacci_hs = fromIntegral . fibonacci . fromIntegral
foreign export ccall fibonacci_hs :: CInt -> CInt
But the C file is different, doesn't have a main
function
test.c
#include <HsFFI.h>
#ifdef __GLASGOW_HASKELL__
#include "Safe_stub.h"
#endif
int foo()
{
int i;
// some dummy values for argc and argv
hs_init(&argc, &argv);
i = fibonacci_hs(42);
hs_exit();
return i;
}
Now to link them together into a .o file I tried something like this:
ghc -fPIE -c -O Safe.hs
emits a Safe_stub.h
, Safe.o
and Safe.hi
files.
Next, I compile the test.c
file into an object file
ghc --make -fPIE -no-hs-main -optc-O -c test.c -o test.o
gives me the test.o
object file that I am looking for.
Now, I attempt to link the two object files produced and I use ghc's linker (that internally uses gcc):
ghc -no-hs-main -optc-nostartfiles -optc-static-pie test.o Safe.o -o result.o
Here is what I get
/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crt1.o:function _start: error: undefined reference to 'main'
collect2: error: ld returned 1 exit status
`x86_64-linux-gnu-gcc' failed in phase `Linker'. (Exit code: 1)
So, it seems somewhere the object files have a reference to main
. Is it a matter of passing some flags? Or what am I doing wrong? Thanks in advance!
EDIT
A suggestion was made to use plain ld -r
to link the object files. The problem with that approach is that if vanilla ld
is used, it will not link in the Haskell runtime needed to actually make the object file useful and callable by another C program as a library.
If it wasn't clear from my post, I want to create a .o file that can be used as a C library that calls into the Haskell code.
CodePudding user response:
Normally a linker is used to combine multiple object files into a single executable, which naturally must have a main
as its entry point. Apparently it is possible to combine multiple object files into a single object file, with ld -r
. Does GHC have a flag to behave this way? Maybe, but I couldn't immediately find it.
But if you're just going to use GHC to invoke the linker, why not invoke the linker yourself?
ld -r test.o Safe.o -o result.o
CodePudding user response:
Here was my solution to the problem after a lot of playing with various flags
ghc -c -O Safe.hs
ghc -c -no-hs-main -optc-O test.c
I don't know what the --make
flag was adding. Now imagine you distributed the Safe.o
and test.o
produced from the above. To compile it with a C function that actually has a main
method like:
testfoo.c
extern int foo();
int main(){
int k = foo();
return k;
}
you would do:
ghc -no-hs-main testfoo.c test.o Safe.o
The produced executable would have the Haskell runtime linked in of course.