Haskell plugin dynamic linking issue

./open
Hello, I'm working on a Haskell plugin (a shared library) for a C program. When I load the plugin, I get the following error: dlopen() failed: /usr/lib64/ghc-8.0.2/ghc-prim-0.5.0.0/libHSghc-prim-0.5.0.0-ghc8.0.2.so: undefined symbol: stg_thawArrayzh open is just a testing wrapper around dlopen(). I found out that stg_thawArrayzh symbol is exported by the rts, and my library isn't linked to it, and neither is ghc-prim. How do I fix that? Adding -lHSrts-ghc8.0.2 to ld-options doesn't seem to have any effect. Thanks.

I'm not sure if it's the solution to your problem already, but there's a Cabal bug that makes it not pass cc-options and ld-options to GHC as ghc-options correctly: https://github.com/haskell/cabal/issues/4435 You need to additionally pass these options as `ghc-options: "-optl -lyourlib"` if you want to be sure. Further, looking at an example I have around, my cabal `library` output (say I have mypackage.cabal with a "library" in there, it's called "dist/build/libHSmypackage-...so") link against things like libHSghc-prim, but not against the RTS (libHSrts...so). I think this makes sense, because as far as I can tell, the RTS is only linked in when you compile the final executable, and which RTS .so file is chosen depends on whether you link in the standard runtime, the -threaded runtime, the debug runtime, etc. So I believe that when loading Haskell from C you will have to dlopen() the RTS you want explicitly, before loading your own .so file. You probably also need to load the RTS with the RTLD_GLOBAL dlopen flag so that when you later load your .so, it can see the RTS symbols. An alternative is probably to add HSrts-ghc-VERSION to `extra-libraries`, like I do here: https://github.com/nh2/call-haskell-from-anything/blob/0ba6737ea17a45e59704d.... That links it explicitly like you planned to do. According to https://github.com/nh2/call-haskell-from-anything/issues/19#issuecomment-263... there is now a `foreign-library` stanza in Cabal that is supposed to free you of manually having to link/load the RTS. I haven't tried it yet, since at the time that comment was written, it wasn't released yet. Now it might be. Detail documentation of that feature: https://github.com/haskell/cabal/blob/db26fa21cba92097d9fdeb580ddf797c35af8e... Hope this helps! Niklas

On 10/04/17 13:50, Niklas Hambüchen wrote:
I'm not sure if it's the solution to your problem already, but there's a Cabal bug that makes it not pass cc-options and ld-options to GHC as ghc-options correctly:
https://github.com/haskell/cabal/issues/4435
You need to additionally pass these options as `ghc-options: "-optl -lyourlib"` if you want to be sure.
Further, looking at an example I have around, my cabal `library` output (say I have mypackage.cabal with a "library" in there, it's called "dist/build/libHSmypackage-...so") link against things like libHSghc-prim, but not against the RTS (libHSrts...so). I think this makes sense, because as far as I can tell, the RTS is only linked in when you compile the final executable, and which RTS .so file is chosen depends on whether you link in the standard runtime, the -threaded runtime, the debug runtime, etc.
So I believe that when loading Haskell from C you will have to dlopen() the RTS you want explicitly, before loading your own .so file. You probably also need to load the RTS with the RTLD_GLOBAL dlopen flag so that when you later load your .so, it can see the RTS symbols.
An alternative is probably to add HSrts-ghc-VERSION to `extra-libraries`, like I do here: https://github.com/nh2/call-haskell-from-anything/blob/0ba6737ea17a45e59704d.... That links it explicitly like you planned to do.
According to https://github.com/nh2/call-haskell-from-anything/issues/19#issuecomment-263... there is now a `foreign-library` stanza in Cabal that is supposed to free you of manually having to link/load the RTS. I haven't tried it yet, since at the time that comment was written, it wasn't released yet. Now it might be.
Detail documentation of that feature: https://github.com/haskell/cabal/blob/db26fa21cba92097d9fdeb580ddf797c35af8e...
Hope this helps!
Niklas
Unfortunately, adding rts to extra-libraries section doesn't help. Now I get a build error from cabal: Process exited with code: ExitFailure 1 Logs have been written to: /home/void/dev/hs/test/.stack-work/logs/test-0.1.log Configuring test-0.1... Cabal-simple_mPHDZzAJ_1.24.2.0_ghc-8.0.2: Missing dependency on a foreign library: * Missing C library: HSrts-ghc8.0.2 This problem can usually be solved by installing the system package that provides this library (you may need the "-dev" version). If the library is already installed but in a non-standard location then you can use the flags --extra-include-dirs= and --extra-lib-dirs= to specify where it is. Relevant cabal file options: extra-lib-dirs: /usr/lib/ghc-8.0.2/rts extra-libraries: HSrts-ghc8.0.2 ghc-options: -Wall -Werror -shared -fPIC -dynamic Note that this is a library, not an executable as in call-haskell-from-anything.

You can try using ghc directly (instead of cabal) so that you have full
control over what flags are passed, like this:
ghc -fPIC -dynamic -package xx -c plugin.hs
ld -shared -Bsymbolic -L<path to rts lib> -lHSrts-ghc8.0.2 -o plugin.so
plugin.o
If this works then you can find what is wrong/different with your cabal
setup by using verbose flags and looking at what is being passed to ghc.
-harendra
On 10 April 2017 at 18:14, Lana Black
Hello,
I'm working on a Haskell plugin (a shared library) for a C program. When I load the plugin, I get the following error:
./open dlopen() failed: /usr/lib64/ghc-8.0.2/ghc-prim-0.5.0.0/libHSghc-prim-0.5.0.0-ghc8.0.2.so: undefined symbol: stg_thawArrayzh
open is just a testing wrapper around dlopen(). I found out that stg_thawArrayzh symbol is exported by the rts, and my library isn't linked to it, and neither is ghc-prim. How do I fix that? Adding -lHSrts-ghc8.0.2 to ld-options doesn't seem to have any effect.
Thanks. _______________________________________________ Haskell-Cafe mailing list To (un)subscribe, modify options or view archives go to: http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe Only members subscribed via the mailman list are allowed to post.

On 10/04/17 18:41, Harendra Kumar wrote:
You can try using ghc directly (instead of cabal) so that you have full control over what flags are passed, like this:
ghc -fPIC -dynamic -package xx -c plugin.hs ld -shared -Bsymbolic -L<path to rts lib> -lHSrts-ghc8.0.2 -o plugin.so plugin.o
If this works then you can find what is wrong/different with your cabal setup by using verbose flags and looking at what is being passed to ghc.
-harendra
Thank you for the idea. That way everything works. I created a repo with a small demo of this issue [1]. For some reason, when options -fPIC -dynamic -shared are present, cabal creates an executable a.out and links in with RTS instead. The shared library on the other hand isn't linked with RTS in any case unless ghc is called directly. ~/devel/hs/cabal-bug> cabal build Package has never been configured. Configuring with default flags. If this fails, please run configure manually. Resolving dependencies... Configuring cabal-bug-0.1... Building cabal-bug-0.1... Preprocessing library cabal-bug-0.1... [1 of 1] Compiling Cabal.Bug ( src/Cabal/Bug.hs, dist/build/Cabal/Bug.o ) Linking a.out ... ~/devel/hs/cabal-bug> ldd a.out linux-vdso.so.1 (0x00007ffc403e6000) libHSrts-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/rts/libHSrts-ghc8.0.2.so (0x00007f62fb591000) libHSbase-4.9.1.0-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/base-4.9.1.0/libHSbase-4.9.1.0-ghc8.0.2.so (0x00007f62fa987000) libHSinteger-gmp-1.0.0.1-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/integer-gmp-1.0.0.1/libHSinteger-gmp-1.0.0.1-ghc8.0.2.so (0x00007f62fa751000) libHSghc-prim-0.5.0.0-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/ghc-prim-0.5.0.0/libHSghc-prim-0.5.0.0-ghc8.0.2.so (0x00007f62fa366000) libgmp.so.10 => /usr/lib64/libgmp.so.10 (0x00007f62fa0c8000) libm.so.6 => /lib64/libm.so.6 (0x00007f62f9dc5000) libc.so.6 => /lib64/libc.so.6 (0x00007f62f9a2c000) librt.so.1 => /lib64/librt.so.1 (0x00007f62f9823000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f62f961f000) libffi.so.6 => /usr/lib64/libffi.so.6 (0x00007f62f9416000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f62f91f9000) /lib64/ld-linux-x86-64.so.2 (0x000055768de76000) ~/devel/hs/cabal-bug> ldd dist/build/libHScabal-bug-0.1-50NTk4EdDNe8utEsR21Nft-ghc8.0.2.so linux-vdso.so.1 (0x00007fff249d9000) libHSbase-4.9.1.0-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/base-4.9.1.0/libHSbase-4.9.1.0-ghc8.0.2.so (0x00007f6c90bb4000) libHSinteger-gmp-1.0.0.1-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/integer-gmp-1.0.0.1/libHSinteger-gmp-1.0.0.1-ghc8.0.2.so (0x00007f6c9097d000) libHSghc-prim-0.5.0.0-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/ghc-prim-0.5.0.0/libHSghc-prim-0.5.0.0-ghc8.0.2.so (0x00007f6c90592000) libgmp.so.10 => /usr/lib64/libgmp.so.10 (0x00007f6c902f5000) libm.so.6 => /lib64/libm.so.6 (0x00007f6c8fff1000) libc.so.6 => /lib64/libc.so.6 (0x00007f6c8fc58000) /lib64/ld-linux-x86-64.so.2 (0x000055f911c1d000) Is this a bug in cabal? [1]: https://github.com/greydot/cabal-bug

I don't know if it's a cabal bug, but when I try to make foreign libraries, I always put them into an `executable` section and use `-no-hs-main -fPIC -shared -dynamic`: https://github.com/nh2/call-haskell-from-anything/blob/d750f9881b4023ead3217...

On 2017-04-10 04:58 PM, Lana Black wrote:
~/devel/hs/cabal-bug> cabal build Package has never been configured. Configuring with default flags. If this fails, please run configure manually. Resolving dependencies... Configuring cabal-bug-0.1...
I am surprised that at this point you did not receive the warning I received: Warning: Instead of 'ghc-options: -lHSrts-ghc8.0.2' use 'extra-libraries: HSrts-ghc8.0.2' In my http://www.vex.net/~trebla/haskell/so.xhtml I use the extra-libraries route (and build-type: Configure to automate "8.0.2"). Also I do not use ghc-options, see below.
Building cabal-bug-0.1... Preprocessing library cabal-bug-0.1... [1 of 1] Compiling Cabal.Bug ( src/Cabal/Bug.hs, dist/build/Cabal/Bug.o ) Linking a.out ...
This a.out is not an executable. It is a shared library. The name "a.out" is a historical generic bland default name for any linker output whenever the user fails to specify "-o realname". Linker output could be executable or library depending on other flags. Now to see why this a.out exists at all, you really should use "cabal build -v" to see how cabal uses GHC. But the short story is: cabal calls GHC at least 3 times: 1st time is to compile *.hs files to *.o files, e.g., Bug.hs to Bug.o. (If you have multiple *.hs files, it is still one call, and you also see a -j2 or -j4 or -j65536 according to your number of CPU cores to tell GHC to parallelize.) 2nd time is to produce the *.a library. 3rd time is to produce the *.so library. Now, if you hardcode the blunt sledgehammer that is ghc-options: -fPIC -dynamic -shared, as opposed to letting cabal do it at the right times (namely, -shared during the 3rd time only), then the extraneous -shared during the 1st time, which is supposed to be *.o only, tricks GHC into calling the linker to produce an extraneous *.so (except there is no -o so the name comes out as a.out). But do not think that you can declare victory now and take this a.out and get away with it. For all practical purposes you will need to include some C code in the library. See again my article for why. But the short story is: Who's gonna call hs_init? The 1st call to GHC does not mention the C code whatsoever. The a.out thus produced will be missing the C code. It is incomplete. There are extra calls after the 1st and before the 2nd to compile *.c to *.o. Only by the 3rd call are all *.o files, from both Haskell and C, brought together into a complete *.so. (Well, the 2nd call too, but you are not going after the *.a.)

On 10/04/17 22:46, Albert Y. C. Lai wrote: > I am surprised that at this point you did not receive the warning I > received: > > Warning: Instead of 'ghc-options: -lHSrts-ghc8.0.2' use > 'extra-libraries: HSrts-ghc8.0.2' > > In my http://www.vex.net/~trebla/haskell/so.xhtml I use the > extra-libraries route (and build-type: Configure to automate "8.0.2"). > Also I do not use ghc-options, see below. Thank you for this suggestion. Now things start to get rather strange. I have changed the cabal file accordingly [1]. When I run cabal configure, I get the following error: > cabal configure Resolving dependencies... Configuring cabal-bug-0.1... cabal: Missing dependency on a foreign library: * Missing C library: HSrts-ghc8.0.2 This problem can usually be solved by installing the system package that provides this library (you may need the "-dev" version). If the library is already installed but in a non-standard location then you can use the flags --extra-include-dirs= and --extra-lib-dirs= to specify where it is. However, that doesn't prevent cabal build from succeeding, and now I have a shared library linked to RTS. > cabal build Building cabal-bug-0.1... Preprocessing library cabal-bug-0.1... [1 of 1] Compiling Cabal.Bug ( src/Cabal/Bug.hs, dist/build/Cabal/Bug.o ) > ldd dist/build/libHScabal-bug-0.1-50NTk4EdDNe8utEsR21Nft-ghc8.0.2.so linux-vdso.so.1 (0x00007ffea75c9000) libHSrts-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/rts/libHSrts-ghc8.0.2.so (0x00007f8057193000) libHSbase-4.9.1.0-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/base-4.9.1.0/libHSbase-4.9.1.0-ghc8.0.2.so (0x00007f8056589000) libHSinteger-gmp-1.0.0.1-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/integer-gmp-1.0.0.1/libHSinteger-gmp-1.0.0.1-ghc8.0.2.so (0x00007f8056353000) libHSghc-prim-0.5.0.0-ghc8.0.2.so => /usr/lib64/ghc-8.0.2/ghc-prim-0.5.0.0/libHSghc-prim-0.5.0.0-ghc8.0.2.so (0x00007f8055f68000) libgmp.so.10 => /usr/lib64/libgmp.so.10 (0x00007f8055cca000) libm.so.6 => /lib64/libm.so.6 (0x00007f80559c7000) libc.so.6 => /lib64/libc.so.6 (0x00007f805562e000) librt.so.1 => /lib64/librt.so.1 (0x00007f8055425000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f8055221000) libffi.so.6 => /usr/lib64/libffi.so.6 (0x00007f8055018000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f8054dfb000) /lib64/ld-linux-x86-64.so.2 (0x000056212e051000) However, if I run cabal build without calling cabal configure first, the latter fails and the build process is stopped at that point. This also prevents me from building my project with stack. >> Building cabal-bug-0.1... >> Preprocessing library cabal-bug-0.1... >> [1 of 1] Compiling Cabal.Bug ( src/Cabal/Bug.hs, >> dist/build/Cabal/Bug.o ) >> Linking a.out ... > > This a.out is not an executable. It is a shared library. The name > "a.out" is a historical generic bland default name for any linker output > whenever the user fails to specify "-o realname". Linker output could be > executable or library depending on other flags. > > Now to see why this a.out exists at all, you really should use "cabal > build -v" to see how cabal uses GHC. But the short story is: > > cabal calls GHC at least 3 times: > > 1st time is to compile *.hs files to *.o files, e.g., Bug.hs to Bug.o. > (If you have multiple *.hs files, it is still one call, and you also see > a -j2 or -j4 or -j65536 according to your number of CPU cores to tell > GHC to parallelize.) > > 2nd time is to produce the *.a library. > > 3rd time is to produce the *.so library. > > Now, if you hardcode the blunt sledgehammer that is ghc-options: -fPIC > -dynamic -shared, as opposed to letting cabal do it at the right times > (namely, -shared during the 3rd time only), then the extraneous -shared > during the 1st time, which is supposed to be *.o only, tricks GHC into > calling the linker to produce an extraneous *.so (except there is no -o > so the name comes out as a.out). Thanks for this explanation. > But do not think that you can declare victory now and take this a.out > and get away with it. > > For all practical purposes you will need to include some C code in the > library. See again my article for why. But the short story is: Who's > gonna call hs_init? Yes, I'm aware of this. The real code in question has calls to hs_init() and hs_exit(). All I'm trying to do now is to solve the linking problem. [1]: https://github.com/greydot/cabal-bug/blob/extra-lib/cabal-bug.cabal

On 2017-04-11 03:20 AM, Lana Black wrote: >> cabal configure > Resolving dependencies... > Configuring cabal-bug-0.1... > cabal: Missing dependency on a foreign library: > * Missing C library: HSrts-ghc8.0.2 > This problem can usually be solved by installing the system package that > provides this library (you may need the "-dev" version). If the library is > already installed but in a non-standard location then you can use the flags > --extra-include-dirs= and --extra-lib-dirs= to specify where it is. > > However, that doesn't prevent cabal build from succeeding, and now I > have a shared library linked to RTS. [...] > However, if I run cabal build without calling cabal configure first, the > latter fails and the build process is stopped at that point. This also > prevents me from building my project with stack. Here is the thing. I cannot reproduce either outcome.

On 13/04/17 18:42, Albert Y. C. Lai wrote: > On 2017-04-11 03:20 AM, Lana Black wrote: >>> cabal configure >> Resolving dependencies... >> Configuring cabal-bug-0.1... >> cabal: Missing dependency on a foreign library: >> * Missing C library: HSrts-ghc8.0.2 >> This problem can usually be solved by installing the system package that >> provides this library (you may need the "-dev" version). If the >> library is >> already installed but in a non-standard location then you can use the >> flags >> --extra-include-dirs= and --extra-lib-dirs= to specify where it is. >> >> However, that doesn't prevent cabal build from succeeding, and now I >> have a shared library linked to RTS. > > [...] > >> However, if I run cabal build without calling cabal configure first, the >> latter fails and the build process is stopped at that point. This also >> prevents me from building my project with stack. > > Here is the thing. I cannot reproduce either outcome. > I dug a bit further, and it looks like the cabal configure error is somehow related to Gentoo, since I'm only able to reproduce it there. Otherwise the solution you linked works perfectly.

On 13/04/17 18:42, Albert Y. C. Lai wrote: > On 2017-04-11 03:20 AM, Lana Black wrote: >>> cabal configure >> Resolving dependencies... >> Configuring cabal-bug-0.1... >> cabal: Missing dependency on a foreign library: >> * Missing C library: HSrts-ghc8.0.2 >> This problem can usually be solved by installing the system package that >> provides this library (you may need the "-dev" version). If the >> library is >> already installed but in a non-standard location then you can use the >> flags >> --extra-include-dirs= and --extra-lib-dirs= to specify where it is. >> >> However, that doesn't prevent cabal build from succeeding, and now I >> have a shared library linked to RTS. > > [...] > >> However, if I run cabal build without calling cabal configure first, the >> latter fails and the build process is stopped at that point. This also >> prevents me from building my project with stack. > > Here is the thing. I cannot reproduce either outcome. Disregard my previous email. The cause of this issue is that cabal tries to link a test executable with only libraries specified in extra-libs. Therefore the test build fails, because RTS isn't linked to base or ghc-prim. Now, here's the fun part. Because the test code supplied by cabal has no actual calls to any library functions, ld may filter out any unused libraries, which includes RTS in our case. This behaviour is controlled by --as-needed option and is enabled by default in Ubuntu, which, I assume, you are using. In my case, the workaround here would be to enable --as-needed by default in the system.
participants (4)
-
Albert Y. C. Lai
-
Harendra Kumar
-
Lana Black
-
Niklas Hambüchen