I try to create a Haskell binding for Xapian. But when GHC tries to link the binding it fails with some linking issues.
Here's a part of the header file:
// file: cbits/libhsxapian.h
#ifdef __cplusplus
extern "C" {
#endif
Xapian::Document* xapian_document_C0();
void xapian_document_D0(Xapian::Document* doc);
Xapian::SimpleStopper* xapian_simplestopper_C0();
void xapian_simplestopper_D0(Xapian::SimpleStopper* stopper);
#ifdef __cplusplus
}
#endif
And the corresponding source file:
// file: cbits/libhsxapian.cpp
#include <xapian.h>
#include "hsxapian.h"
Xapian::Document* xapian_document_C0() {
return new Xapian::Document();
}
void xapian_document_D0(Xapian::Document* doc) {
delete doc;
}
Xapian::SimpleStopper* xapian_simplestopper_C0() {
return new Xapian::SimpleStopper();
}
void xapian_simplestopper_D0(Xapian::SimpleStopper* stopper) {
delete stopper;
}
And the Haskell source file containing the FFI code:
-- file: src/Bindings/Xapian.hs
{-# LANGUAGE ForeignFunctionInterface #-}
module Bindings.Xapian where
import Foreign
import Foreign.C
type DocumentPtr = Ptr ()
foreign import ccall "hsxapian.h xapian_document_C0"
xapian_document_C0 :: IO DocumentPtr
foreign import ccall "hsxapian.h &xapian_document_D0"
xapian_document_D0 :: FunPtr (DocumentPtr -> IO ())
type StopperPtr = Ptr ()
foreign import ccall "hsxapian.h xapian_simplestopper_C0"
xapian_simplestopper_C0 :: IO StopperPtr
foreign import ccall "hsxapian.h &xapian_simplestopper_D0"
xapian_simplestopper_D0 :: FunPtr (StopperPtr -> IO ())
Among other options the cabal file contains:
include-dirs:
cbits
cxx-sources:
cbits/hsxapian.cpp
extra-libraries:
stdc++
When I try to compile that package I get the following error message:
<command line>: /home/user/hsxapian/.stack-work/dist/x86_64-linux-tinfo6/Cabal-3.0.1.0/build/libHShsxapian-0.1.0.0-6mEQUi65qY9HKkEuYL7SJz-ghc8.8.3.so: undefined symbol: _ZTVN6Xapian13SimpleStopperE
Apparently GHC is unable to link the executable contained in the package with the library.
However, using nm I can verify the symbol is present in libxapian:
$ nm -D /usr/lib/libxapian.so | grep _ZTVN6Xapian13SimpleStopperE
0000000000226cf0 V _ZTVN6Xapian13SimpleStopperE
$ nm -D -C /usr/lib/libxapian.so | grep 0000000000226cf0
0000000000226cf0 V vtable for Xapian::SimpleStopper
Interestingly, if I remove xapian_simplestopper_C0 from the header, C++ and Haskell source everything compiles and gets linked as expected. And if I add xapian_document_C0 again it still compiles and gets linked as well.
So my questions are:
Why does GHC choke on
SimpleStopperbut not onDocument?SimpleStopper seems to be an ordinary C++ class like Document.
Why does the second run succeed?
I expected it to fail in the linking phase with the exact same error message. Doesn't the executable get linked again or is GHC somehow able to find the missing symbol?
How do I debug such a linking issue?
I didn't find any valuable resource on this online. Are there some flags I missed in the GHC user guide? Besides that, I'm aware of the following: