I trying to work though some simple FFI things to figure out how to get Rust to work with C or C++ code. My immediate question is:
It seems when I compile Rust code to an object file, it is missing some of the bindings for the clang linker to finalize an executable. Here are two very simple files that I am using.
clink.c
#include <stdio.h>
#include <stdbool.h>
int rust_int(int);
bool rust_bool(int, int);
/********************************/
/* void rust_char_star(char *); */
/********************************/
int main(void) {
printf("%d\n", rust_int(5));
if (rust_bool(5, 6)) {
printf("True\n");
}
else { printf("False\n"); }
/******************************/
/* rust_char_star("Testing"); */
/******************************/
return 0;
}
rustlink.rs
use std::os::raw::{c_int, c_char};
// use std::ffi::{CStr, CString};
#[no_mangle]
pub extern "C" fn rust_int(i: c_int) -> c_int {
i
}
#[no_mangle]
pub extern "C" fn rust_bool(x: c_int, y: c_int) -> bool {
if x > y {
true
}
else {
false
}
}
////////////////////////////////////////////////////////////////////////////
// #[no_mangle] //
// pub extern "C" fn rust_char_star(c: *const c_char) { //
// let str_printable = unsafe {CString::from_raw(c as *mut c_char) }; //
// //
// println!("{:?}", str_printable); //
// } //
////////////////////////////////////////////////////////////////////////////
When I compile each of these without the CString
, everything works as expected. Ignore the warning.
~/dev/rust/learn/clink$ rustc --emit obj --crate-type staticlib rustlink.rs
warning: unused import: `c_char`
use std::os::raw::{c_int, c_char};
^^^^^^
= note: #[warn(unused_imports)] on by default
~/dev/rust/learn/clink$ clang clink.c rustlink.o -o test
~/dev/rust/learn/clink$ ./test
5
False
When I uncomment all the code to use the type CString
though, that's where I'm missing something. I'm not going to repost the above code with the comments removed. Here is what the output yields though.
~/dev/rust/learn/clink$ rustc --emit obj --crate-type staticlib rustlink.rs
warning: unused import: `CStr`
--> rustlink.rs:2:16
|
2 | use std::ffi::{CStr, CString};
| ^^^^
|
= note: #[warn(unused_imports)] on by default
~/dev/rust/learn/clink$ clang clink.c rustlink.o -o test
rustlink.o: In function `alloc::alloc::dealloc':
rustlink.3a1fbbbh-cgu.0:(.text._ZN5alloc5alloc7dealloc17hca8aab9ecdf50cafE+0x43): undefined reference to `__rust_dealloc'
rustlink.o: In function `rust_char_star':
rustlink.3a1fbbbh-cgu.0:(.text.rust_char_star+0xa): undefined reference to `std::ffi::c_str::CString::from_raw'
rustlink.3a1fbbbh-cgu.0:(.text.rust_char_star+0x3a): undefined reference to `<std::ffi::c_str::CString as core::fmt::Debug>::fmt'
rustlink.3a1fbbbh-cgu.0:(.text.rust_char_star+0xa9): undefined reference to `std::io::stdio::_print'
rustlink.o:(.data.DW.ref.rust_eh_personality[DW.ref.rust_eh_personality]+0x0): undefined reference to `rust_eh_personality'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
~/dev/rust/learn/clink$
Going a little further. I attempted to compile the Rust code to a static library to be fed into clang. I'm uncertain why, but this is the result.
~/dev/rust/learn/clink$ rustc --crate-type staticlib rustlink.rs
warning: unused import: `CStr`
--> rustlink.rs:2:16
|
2 | use std::ffi::{CStr, CString};
| ^^^^
|
= note: #[warn(unused_imports)] on by default
~/dev/rust/learn/clink$ ls
clink.c librustlink.a rustlink.o rustlink.rs
~/dev/rust/learn/clink$ clang clink.c -o test -lrustlink
/usr/bin/ld: cannot find -lrustlink
clang: error: linker command failed with exit code 1 (use -v to see invocation)
~/dev/rust/learn/clink$ clang clink.c -o test -L -lrustlink
/tmp/clink-357672.o: In function `main':
clink.c:(.text+0x15): undefined reference to `rust_int'
clink.c:(.text+0x3a): undefined reference to `rust_bool'
clink.c:(.text+0x83): undefined reference to `rust_char_star'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
~/dev/rust/learn/clink$
This is where I am a little confused. I thought when you tell rustc that the crate-type is static that it includes all dependencies, but I guess that is not the case.
Clearly I am missing a step. I would prefer that rustc include all bindings so I can just use a simple command to clang to produce an executable.