I have a module Main
in Main.hs
. The program uses the FFI (in particular, FunPtr
).
When I run stack exec ghci
and I load the module (:l src/Main.hs
), it works perfectly.
However when I compile the module to an executable and I run the executable, I get a crash, namely a segmentation fault.
Therefore I'm wondering whether I need to compile with a certain option. Is there a specific option to use when dealing with the FFI ? I tried -O0
, -fllvm
, no way. Maybe stack exec ghci
includes an option that could be used with GHC ?
Also, is there a debug option that one can set to GHC in order to run the executable with gdb
? I tried the -g
option but gdb
does not find debugging symbols. EDIT This point is solved: gdb
finds debugging symbols when I compile with stack exec -- ghc -g -rtsopts src/Main.hs
.
I'm on Linux Ubuntu and I'm using GHC 8.2.2.
Here is a minimal program that reflects the structure of my real program. This one works fine (in GHCI or as an executable), but I include it nevertheless.
helloffi.c:
#include <stdlib.h>
double** evalf(double (*f)(double), double x){
double** out = malloc(2 * sizeof(double*));
for(unsigned i=0; i<2; i++){
out[i] = malloc(2 * sizeof(double));
out[i][0] = (*f)(x);
out[i][1] = (*f)(x+1);
}
return out;
}
double sumpointer(double** pptr){
double x=0;
for(unsigned i=0; i<2; i++){
for(unsigned j=0; j<2; j++){
x += pptr[i][j];
}
}
return x;
}
main module of the library, Lib.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Lib
where
import Foreign.C.Types
import Foreign.Ptr (Ptr, FunPtr, freeHaskellFunPtr)
type CFunction = CDouble -> IO CDouble
foreign import ccall "wrapper" functionPtr
:: CFunction -> IO (FunPtr CFunction)
foreign import ccall "evalf" c_evalf
:: FunPtr CFunction
-> CDouble
-> IO (Ptr (Ptr CDouble))
fun2cfun :: (Double -> Double) -> CFunction
fun2cfun f x =
return $ realToFrac (f (realToFrac x))
evalFun :: (Double -> Double) -> Double -> IO (Ptr (Ptr CDouble))
evalFun f x = do
fPtr <- functionPtr (fun2cfun f)
result <- c_evalf fPtr (realToFrac x)
freeHaskellFunPtr fPtr
return result
foreign import ccall "sumpointer" c_sumpointer
:: Ptr (Ptr CDouble) -> IO CDouble
module Main.hs, to be compiled
module Main
where
import Lib
main :: IO ()
main = do
x <- evalFun (\x -> x*x) 2
y <- c_sumpointer x
print y
Well, this is not really an answer to the question but I have painfully solved the issue and I think it might be helpful for others. It is worth to share. So I post a minimal example showing the problem. A very minimal example.
helloffi/
├── C
│ ├── array.c
│ ├── helloffi.c
│ └── helloffi.h
├── helloffi.cabal
├── Setup.hs
├── src
│ ├── Lib.hs
│ └── Main.hs
└── stack.yaml
array.c: define an array in a C file, for example:
int array[2][3] =
{{1, 24, 1},
{2, 19, 1}};
helloffi.c: define a function that uses this array, for example:
#include "helloffi.h"
int getCoef(unsigned i, unsigned j){
return array[i][j];
}
helloffi.h, the header file:
int array[2][3];
int getCoef(unsigned, unsigned);
That's all for the C part. Now the Haskell part. Make a library module importing the C function.
Lib.hs:
{-# LANGUAGE ForeignFunctionInterface #-}
module Lib
where
import Foreign.C.Types
foreign import ccall "getCoef" c_getCoef
:: CUInt -> CUInt -> IO CInt
Main.hs, to be compiled to an executable:
module Main
where
import Lib
main :: IO ()
main = do
x <- c_getCoef 0 1
print x
That's all. And now, the mystery. Compile the library (to get the module Lib
and the executable generated from Main.hs
, let's say this one is called test
).
stack exec ghci
, gives the correct result (24
):
Prelude> :l src/Main.hs
[1 of 1] Compiling Main ( src/Main.hs, interpreted )
Ok, one module loaded.
*Main> main
24
run the executable, gives a wrong result (always 0
):
$ .stack-work/install/x86_64-linux/lts-11.4/8.2.2/bin/test
0
The test
executable is the compiled Main.hs
. However it does not correctly read the entries of the array in array.c
, while :l src/Main.hs
after stack exec ghci
gives the correct result.
Isn't it weird ? Does anyone has an explanation ?
I don't know yet why the behavior differs between stack exec ghci
and the executable but now one has a solution thanks to @Alec's comments below: it suffices to replace int array[2][3]
in the header file with extern int array[2][3]
. It looks like the executable considers int array[2][3]
as the definition of array
, whose entries are initialized to 0
, while stack exec ghci
considers it as the declaration of array
.