Create shared C object linked to Rust dylib for use in R

1.1k views Asked by At

I am trying to create a shared object I can load into R that calls Rust functions by way of R's C API. To call Rust from C, I am following this blog post. My problem arises when I try to create the shared library and link to the Rust library. The linker complains that it can't find my Rust function. I am quite new to compiled languages and have given this a couple days' worth of effort before turning to SO. In that time I have learned a lot about compiler flags and yet come no closer to a solution. I think it may be something obvious.

My C++ code:

#include "Rinternals.h"
#include "R.h"
#include "treble.h"

// test.cpp
extern "C" {

SEXP triple(SEXP val) {

    int32_t ival = *INTEGER(val);
    Rprintf("9 tripled is %d\n", treble(ival));
    return R_NilValue;
}

}

treble.h:

#include <stdint.h>

int32_t treble(int32_t value);

My Rust code:

#![crate_type = "dylib"]

#[no_mangle]
pub extern fn treble(value: i32) -> i32 {
  value * 3
}

This is what I'm doing on the command line:

$ rustc glue.rs
$ g++ -shared test.cpp -o test.so -I/Library/Frameworks/R.framework/Headers -L/Library/Frameworks/R.framework/Libraries -L. -lR -lglue
Undefined symbols for architecture x86_64:
  "treble(int)", referenced from:
      _triple in test-dac64b.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Inspecting the object file that rust creates:

$ nm -gU libglue.dylib
...
0000000000001750 T _treble
1

There are 1 answers

1
Shepmaster On BEST ANSWER

In the C++ code, you need to declare the Rust function (which is available via the C ABI) as extern "C".

treble.h

#include <stdint.h>

extern "C" {
  int32_t treble(int32_t value);
}

The error you are getting is because the C++ compiler is name mangling the method treble before attempting to link against it. extern "C" disables the mangling.

Additionally, your Rust FFI code should always use the types from the libc crate; in your case you want libc::int32_t.