I’ve had no luck so far with linking a Haskell library to a Rust project. I’ve had a lot of errors, the latest being the recompile with -fPIC
for ghc
.
I’ve managed to get a functional example of dynamically linking — but have been unable to get it linked statically.
Attaching my current setup right now:
build.rs
fn main() {
println!("cargo:rustc-link-search=native=deps");
println!("cargo:rustc-link-lib=static=tesths");
}
src/main.rs
extern "C" {
pub fn addTwo(x: i32) -> i32;
pub fn init();
pub fn fin();
}
fn main() {
println!("Hello, world!");
}
src/haskell/Lib.hs
module Lib where
import Foreign.C.Types
addTwo :: CInt -> CInt
addTwo = (2 + )
foreign export ccall addTwo :: CInt -> CInt
cwrapper.c
#include <HsFFI.h>
#ifdef __GLASGOW_HASKELL__
#include "Lib_stub.h"
#endif
#include <stdio.h>
void init(void) {
static char *argv[] = {"libhs.so", 0}, **argv_ = argv;
static int argc = 1;
hs_init(&argc, &argv_);
}
void fin(void) { hs_exit(); }
I compile #4 with ghc -c -static -lHSrts -lffi cwrapper.c
, getting cwrapper.o
. Similarly, I compile #3 with ghc -c -static -fPIC -lffi Lib.hs
and get object code for that as well.
Once that is done, I go ahead and archive the two with ar rcs libtesths.a Lib.o cwrapper.o
.
On cargo build
:
note: /usr/bin/ld: deps/libtesths.a(Lib.o):(.text+0x29): undefined reference to `newCAF'
/usr/bin/ld: deps/libtesths.a(Lib.o):(.text+0x39): undefined reference to `stg_bh_upd_frame_info'
/usr/bin/ld: deps/libtesths.a(Lib.o):(.text+0x48): undefined reference to `base_ForeignziCziTypes_zdfNumCInt_closure'
/usr/bin/ld: deps/libtesths.a(Lib.o):(.text+0x4f): undefined reference to `stg_ap_p_info'
…
I’ve got a hunch that libHSrts isn’t being linked statically for some reason.
I followed Shepmaster’s advice and successfully made a working example. But I’m having issues linking with my rust exec now.
I used stack with ghc-options: -staticlib -stubdir .
and c-sources: cwrapper.c
to build. When I try to build the rust project with cargo rustc — -C relocation-model=static
:
/usr/bin/ld: deps/liba.a(Type.o): in function `integerzmwiredzmin_GHCziIntegerziType_zdwplusBigNatWord_info'
(.text.integerzmwiredzmin_GHCziIntegerziType_zdwplusBigNatWord_info+0x128): undefined reference to `__gmpn_add_1'
/usr/bin/ld: deps/liba.a(Type.o): in function
`integerzmwiredzmin_GHCziIntegerziType_zdwminusBigNatWord_info'
(.text.integerzmwiredzmin_GHCziIntegerziType_zdwminusBigNatWord_info+0xdf): undefined reference to `__gmpn_sub_1'
/usr/bin/ld: deps/liba.a(Type.o): in function `integerzmwiredzmin_GHCziIntegerziType_complementInteger_info'
(.text.integerzmwiredzmin_GHCziIntegerziType_complementInteger_info+0x138): undefined reference to `__gmpn_sub_1'
/usr/bin/ld: deps/liba.a(Type.o): in function `integerzmwiredzmin_GHCziIntegerziType_zdwtimesBigNatWord_info'
(.text.integerzmwiredzmin_GHCziIntegerziType_zdwtimesBigNatWord_info+0x158): undefined reference to `__gmpn_mul_1'
There is also mention of a wrappers.o
. Any help is appreciated.
I solved the gmp
issues by setting build.rs
to link libgmp
as well. I have a different problem now, though.
note: /usr/bin/ld: deps/libhssource.a(Lib.o): in function `testFFI':
(.text+0x88fd): multiple definition of `testFFI'; deps/libhssource.a(Lib.o):(.text+0x1b66): first defined here
/usr/bin/ld: deps/libhssource.a(cwrapper.o): in function `init':
cwrapper.c:(.text+0x0): multiple definition of `init'; deps/libhssource.a(cwrapper.o):cwrapper.c:(.text+0x0): first defined here
/usr/bin/ld: deps/libhssource.a(cwrapper.o): in function `fin':
cwrapper.c:(.text+0x20): multiple definition of `fin'; deps/libhssource.a(cwrapper.o):cwrapper.c:(.text+0x1a): first defined here
collect2: error: ld returned 1 exit status
This the final build command that does the trick:
cargo rustc -- -C relocation-model=static -Clink-arg=-Wl,--allow-multiple-definition
Using RUSTFLAGS
will cause the build to crash because it builds all the dependencies with a static relocation model, which won’t work — in my case — for the rustversion
crate. It will report a recompile with -fPIC
error.
I compiled the Haskell library and C shim at once, passing the -staticlib
flag:
ghc -c -staticlib Lib.hs cwrapper.c -o libtesths.a
I then called the functions:
extern "C" {
pub fn addTwo(x: i32) -> i32;
pub fn init();
pub fn fin();
}
fn main() {
unsafe {
init();
println!("{}", addTwo(40));
fin();
}
}
% cargo run -q
42
This worked for me on macOS 12.0.1 on an Apple Silicon processor using GHC 8.10.7.
If you are on x86_64 Linux, you may need to add RUSTFLAGS='-C relocation-model=static'
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With