[Haskell-cafe] Use Haskell shared library with foreign exports with dlopen/dlsym
Albert Y. C. Lai
trebla at vex.net
Wed Sep 10 19:05:14 UTC 2014
This is going to be very cunning.
hs_init is not needed in this case because the executable comes from
ghc --make -dynamic test.hs
The "-dynamic" causes the exemption. This is similar to the case of ghci
7.8 on Linux.
(I agree that there are differences too. For now, the similarity
dominates. The executable has already brought in a dynamic RTS, and it
has already been hs_inited and running. Subsequently, any dynamic
library that looks for a dynamic RTS will simply find the running one.)
Here is the story of the segfault:
1. "foreign export" causes the following dynamic library constructor to
be included in the dynamic library:
static void stginit_export_Test_zdfstableZZC0ZZCmainZZCTestZZCtest()
__attribute__((constructor));
static void stginit_export_Test_zdfstableZZC0ZZCmainZZCTestZZCtest()
{
foreignExportStablePtr((StgPtr)
&Test_zdfstableZZC0ZZCmainZZCTestZZCtest_closure);
}
foreignExportStablePtr is in the RTS (and we do have a dynamic RTS
already running). Test_zdfstableZZC0ZZCmainZZCTestZZCtest_closure is in
the dynamic library representing the test::Int in module Test.
I found out by -keep-tmp-files and examining the tmp files.
2. dlopen causes a mmap, and the closure's address is in that mmap region.
3. dlopen also causes the constructor to be invoked. I suppose it means
a StablePtr is created, therefore the closure's address is registered in
the global table of StablePtrs.
4. dlclose causes a munmap. The closure's address is now invalid.
5. Garbage collection visits everything registered in the global table
of StablePtrs (among other roots). But one of the registered addresses
is invalid. Segfault.
I found out by asking gdb for backtrace. So in fact I can specifically
point at "evacuate()".
At this point, my recommendation:
Don't use "-dynamic" on the executable. Do it static.
This implies you will have two RTSes, the static one used by the
executable for itself, and the dynamic one used by the dynamic library
for itself.
This is a good thing because the static GCtor will never visit the
closure in the dynamic library, hell it will never hear of it.
The dynamic library will find and use foreignExportStablePtr from the
dynamic RTS only. It doesn't see the static RTS, or any static library
for that matter.
The dynamic GCtor is the only one who knows of the closure, but this
GCtor no longer runs after dlclose, hell it no longer exists.
The down side is that you now have to do the full thing to the dynamic
library:
http://www.vex.net/~trebla/haskell/so.xhtml
section "Making The Library"
P.S. I believe that those things about hs_add_root and __stginit_* are
outdated.
On 14-09-08 12:47 PM, Carter Schonwald wrote:
> what OS and other things?
> what does ghc --info say?
> Also, when calling haskell code as if it were C code, you need to init
> the RTS its using, its not going to magically know you're linking to it
> from haskell.(though maybe you can arrange things to use the pre inited
> hs runtime, but i'm not familiar with how to do so)
> http://www.haskell.org/haskellwiki/Calling_Haskell_from_C
>
> On Mon, Sep 8, 2014 at 10:05 AM, Tobias Neumann <mail at tobias-neumann.eu
> <mailto:mail at tobias-neumann.eu>> wrote:
>
> Hello Cafe,
>
> I am trying to use a Haskell shared library with foreign exports from
> Haskell again via dlopen/dlsym. Sadly it segfaults, and the segfaults
> happen on dlclose during garbage collection points (as figured out by
> monochrom in #haskell). So right now I can only open a library once and
> may not dlclose it. Can someone point me to a mistake I made, or is this
> rather a ghc (7.8.3) bug? Please see attached minimal example.
>
> Regards, Tobias
>
>
> test.hs:
> module Main where
>
> import qualified System.Posix.DynamicLinker as DL
> import Foreign
>
> foreign import ccall "dynamic"
> mkTest :: FunPtr Int -> Int
>
> main = do
> DL.withDL ("./libtest.so") [DL.RTLD_NOW] $ \dl -> do
> dimPtr <- DL.dlsym dl "test"
> let test = mkTest dimPtr
> print test
>
> libtest.hs:
> module Test() where
>
> import Foreign
>
> foreign export ccall test :: Int
>
> test :: Int
> test = 124
>
> build with:
> ghc --make -shared -dynamic -fPIC libtest.hs -o libtest.so
> ghc --make -dynamic test.hs
>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org <mailto:Haskell-Cafe at haskell.org>
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>
>
>
>
> _______________________________________________
> Haskell-Cafe mailing list
> Haskell-Cafe at haskell.org
> http://www.haskell.org/mailman/listinfo/haskell-cafe
>
More information about the Haskell-Cafe
mailing list