Home > Net >  Creating and linking a .o file with Haskell foreign function export and no main function in C
Creating and linking a .o file with Haskell foreign function export and no main function in C

Time:10-29

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.

  • Related