How do I produce an FFI function with a `main`-like interface? - rust

I am trying to use rust to interface with an ancient platform that expects the functions to be declared and externed in a specific C-Style format.
char *func(int argc, char *argv[])
I am trying to reproduce this signature in Rust. I need to be able to properly parse arguments (ideally as Rust Strings) and then return an arbitrary value as a string without disrupting the signature.
So far, I have the following:
#[no_mangle]
pub extern "C" fn my_function(argc: isize, argv: *const c_char) -> (CString) {
let output: CString = CString::new("Test Output").expect("CString::new failed");
return output;
}
Which properly returns my test string through the interface, but I can't figure out how to parse argv into a usable format, or if my implementation of argv is even properly compliant with the specified format. I am not familiar with pointers, and I am not even sure where to start with figuring this out.

With your existing code, you have a few primary issues:
argv: *const c_char is wrong because the original C code is an array of pointers, not a pointer to a char. The correct type is
argv: *const *const c_char
You cannot return CString because that is not a type that C will know what to do with. You need to return *mut c_char just like the C function did. To do this, you use output.into_raw() to unwrap the string and get the underlying *mut c_char pointer. This also means that you are giving this C code a random pointer, and it can read it as a C string, but it can't free that memory, so you're either leaking that memory, or you need a second function to pass the string back to Rust to be freed later using from_raw. In this case I'm using *mut c_char because that's what into_raw returns.
You should consistently use the c_ types for everything in the C interface, including argc, e.g.
my_function(argc: c_int, argv: *const *const c_char) -> *mut c_char
To process the args, you want to loop through the argc values to wrap each pointer into a CStr, which you can then work with without needing to worry about unsafe code.
All taken together:
#[no_mangle]
pub extern "C" fn my_function(argc: c_int, argv: *const *const c_char) -> *mut c_char {
let argv: Vec<_> = (0..argc)
.map(|i| unsafe { CStr::from_ptr(*argv.add(i as usize)) })
.collect();
// Whatever processing you need to do.
let output = CString::new("Test Output").expect("CString::new failed");
return output.into_raw();
}
and potentially
#[no_mangle]
pub extern "C" fn my_function_free(s: *mut c_char) {
unsafe {
CString::from_raw(s);
}
}

Related

Using reference to type parameter in callback function of C library

I am writing a Rust interface to a C library that has a function with the following signature:
typedef int (*callback_t)(const int *a, void *user_data);
void execute(callback_t callback);
What I would like is that the users of the Rust interface can pass any type T for user_data (user_data is not used in the C library). In other words, on the Rust side I would like:
type Callback<T> = fn(a: &mut usize, user_data: &mut T) -> usize;
I tried casting a user-defined Rust function of type Callback<T> to
extern "C" fn callback(a: *mut c_int, user_data: *mut c_void) -> c_int
with as, but that does not work. I also tried to create a wrapping closure. Both attempts did not work.
Can anyone help me out?
You should not be casting function pointers between different signatures. That's catastrophically unsafe and will blow your program up (if you're lucky). Function pointers are not interchangeable, and the compiler cannot magically make them compatible.
What you're effectively doing here is taking an order written in Italian, scratching out "language = Italian", replacing it with "language = Russian", and expecting the Russian chef to understand it because, hey, it says it's in Russian!
First of all, your translation of the raw C type is probably wrong. The first argument is *const c_int, not *mut c_int. C does allow you to cast const away, but it's rarely something the other code will expect.
Secondly, you should not translate raw C pointers as safe Rust references. If the C code calls with a null pointer, your Rust code will have undefined behaviour. Unless the C library guarantees that both pointers are never null with a contract signed in blood and guaranteed with the programmer's first born child, don't trust it: check the pointers first.
Third, c_int and usize are not the same type. Do not conflate them. The correct type to use for the Rust interface is c_int.
So the actual C callback type in Rust is:
type CCallback = Option<extern "C" fn(a: *const c_int, user_data: *mut c_void) -> c_int>;
The Option is there because C function pointers can be null, in Rust they can't.
Finally, Callback<T> isn't marked with extern "C". Getting the calling convention exactly right is of critical importance.
The signature of any function you intend to cast to the C callback type should be exactly the C callback signature. That is:
extern "C" fn a_callback(a: *const c_int, user_data: *mut c_void) -> c_int {
::std::process::abort();
}
Now, you might be able to get away with this:
extern "C" fn a_callback<T>(a: *const c_int, user_data: *mut T) -> c_int {
::std::process::abort();
}
And coerce Some(a_callback) to CCallback. That said, I can't guarantee this is correct for all possible T.
To be safe, you should explicitly wrap all Rust callback functions in a translation function. This is most easily done with a macro that, given the name of the Rust function, generates a C shim.
macro_rules! shim {
($c:ident => $r:ident) => {
extern "C" fn $c(a: *const c_int, user_data: *mut c_void) -> c_int {
if a.is_null() {
::std::process::abort()
}
if user_data.is_null() {
::std::process::abort()
}
// NOTE: You need to make *absolutely certain* that this cast
// of user_data is valid.
let res: i32 = $r(&*a, &mut *(user_data as *mut _));
res as c_int
}
};
}
shim!(another_callback_c => another_callback);
fn another_callback(a: &c_int, user_data: &mut u8) -> i32 {
// Do something...
::std::process::abort()
}

How to create and return a C++ struct from Rust FFI?

I'm trying to create and return a C++ struct. I am currently getting a cannot move out of dereference of raw pointer error when I try to compile. Any idea how I can make this work?
#![allow(non_snake_case)]
#![allow(unused_variables)]
extern crate octh;
// https://thefullsnack.com/en/string-ffi-rust.html
use std::ffi::CString;
#[no_mangle]
pub unsafe extern "C" fn Ghelloworld(
shl: *const octh::root::octave::dynamic_library,
relative: bool,
) -> *mut octh::root::octave_dld_function {
let name = CString::new("helloworld").unwrap();
let pname = name.as_ptr() as *const octh::root::std::string;
std::mem::forget(pname);
let doc = CString::new("Hello World Help String").unwrap();
let pdoc = doc.as_ptr() as *const octh::root::std::string;
std::mem::forget(pdoc);
return octh::root::octave_dld_function_create(Some(Fhelloworld), shl, pname, pdoc);
}
pub unsafe extern "C" fn Fhelloworld(
args: *const octh::root::octave_value_list,
nargout: ::std::os::raw::c_int,
) -> octh::root::octave_value_list {
let list: *mut octh::root::octave_value_list = ::std::ptr::null_mut();
octh::root::octave_value_list_new(list);
std::mem::forget(list);
return *list;
}
I'm trying to create and return a C++ struct
You cannot; C++ (like Rust) does not have a stable, defined ABI. There is no way in Rust to specify that a structure has repr(C++), therefore you cannot create such a structure, much less return it.
The only stable ABI is the one presented by C. You can define structs as repr(C) to be able to return them directly:
extern crate libc;
use std::ptr;
#[repr(C)]
pub struct ValueList {
id: libc::int32_t,
}
#[no_mangle]
pub extern "C" fn hello_world() -> ValueList {
let list_ptr = ::std::ptr::null_mut();
// untested, will cause segfault unless list_ptr is set
unsafe { ptr::read(list_ptr) }
}
That method is highly suspicious though; normally you'd see it as
#[no_mangle]
pub extern "C" fn hello_world() -> ValueList {
unsafe {
let mut list = mem::uninitialized();
list_initialize(&mut list);
list
}
}
See also:
Is it possible to use a C++ library from Rust when the library uses templates (generics)?
I encourage you to read my Rust FFI Omnibus.

How to expose a Rust `Vec<T>` to FFI?

I'm trying to construct a pair of elements:
array: *mut T
array_len: usize
array is intended to own the data
However, Box::into_raw will return *mut [T]. I cannot find any info on converting raw pointers to slices. What is its layout in memory? How do I use it from C? Should I convert to *mut T? If so, how?
If you just want some C function to mutably borrow the Vec, you can do it like this:
extern "C" {
fn some_c_function(ptr: *mut i32, len: ffi::size_t);
}
fn safe_wrapper(a: &mut [i32]) {
unsafe {
some_c_function(a.as_mut_ptr(), a.len() as ffi::size_t);
}
}
Of course, the C function shouldn't store this pointer somewhere else because that would break aliasing assumptions.
If you want to "pass ownership" of the data to C code, you'd do something like this:
use std::mem;
extern "C" {
fn c_sink(ptr: *mut i32, len: ffi::size_t);
}
fn sink_wrapper(mut vec: Vec<i32>) {
vec.shrink_to_fit();
assert!(vec.len() == vec.capacity());
let ptr = vec.as_mut_ptr();
let len = vec.len();
mem::forget(vec); // prevent deallocation in Rust
// The array is still there but no Rust object
// feels responsible. We only have ptr/len now
// to reach it.
unsafe {
c_sink(ptr, len as ffi::size_t);
}
}
Here, the C function "takes ownership" in the sense that we expect it to eventually return the pointer and length to Rust, for example, by calling a Rust function to deallocate it:
#[no_mangle]
/// This is intended for the C code to call for deallocating the
/// Rust-allocated i32 array.
unsafe extern "C" fn deallocate_rust_buffer(ptr: *mut i32, len: ffi::size_t) {
let len = len as usize;
drop(Vec::from_raw_parts(ptr, len, len));
}
Because Vec::from_raw_parts expects three parameters, a pointer, a size and a capacity, we either have to keep track of the capacity as well somehow, or we use Vec's shrink_to_fit before passing the pointer and length to the C function. This might involve a reallocation, though.
You could use [T]::as_mut_ptr to obtain the *mut T pointer directly from Vec<T>, Box<[T]> or any other DerefMut-to-slice types.
use std::mem;
let mut boxed_slice: Box<[T]> = vector.into_boxed_slice();
let array: *mut T = boxed_slice.as_mut_ptr();
let array_len: usize = boxed_slice.len();
// Prevent the slice from being destroyed (Leak the memory).
mem::forget(boxed_slice);

Is it possible to call a Rust function taking a Vec from C?

Suppose I have the following Rust library:
// lib.rs
#![crate_type = staticlib]
#[no_mangle]
pub extern fn do_something(number: i32) {
// something
}
#[no_mangle]
pub extern fn do_something_else(collection: &Vec<i32>) {
// something
}
I know that, to call do_something from C, I'd just need to declare an extern function taking an int32_t, but is it possible to call do_something_else? If so, how?
You can, but the better question is should you?
Since you cannot construct a Vec from C, you'd have to construct it in Rust and then return a pointer to C. C code would own the pointer to the Vec and would then pass it back when calling do_something_else.
Then there's the problem that you can't really modify the Vec in C either, other than by creating new FFI methods that mirror all of the Rust methods.
You also probably shouldn't take a &Vec<i32> because Rust references are guaranteed to not be NULL, and there's nothing that enforces that when called from C. It's better to take a *const Vec<i32>, assert that it's non-NULL and convert it to a reference.
Chances are that you want to accept a C array through the FFI boundary. C arrays are a pointer and a length, so you'd accept both and reconstitute a Rust slice (since you wouldn't own the array):
use std::slice;
pub extern fn do_something_else(p: *const i32, len: libc::size_t) {
let slice = unsafe {
assert!(!p.is_null());
slice::from_raw_parts(p, len)
};
}
Obligatory link to The Rust FFI Omnibus.
If you really needed to do what you asked, it would probably look something like this:
extern crate libc;
#[no_mangle]
pub extern fn make_vec() -> *mut Vec<i32> {
Box::into_raw(Box::new(Vec::new()))
}
#[no_mangle]
pub extern fn add_number(vec: *mut Vec<i32>, val: libc::int32_t) {
let vec = unsafe {
assert!(!vec.is_null());
&mut *vec
};
vec.push(val);
}
#[no_mangle]
pub extern fn print_vec(vec: *const Vec<i32>) {
let vec = unsafe {
assert!(!vec.is_null());
&*vec
};
println!("{:?}", vec);
}
#[no_mangle]
pub extern fn drop_vec(vec: *mut Vec<i32>) {
unsafe {
assert!(!vec.is_null());
Box::from_raw(vec);
}
}
And would be used like (untested):
// Add extern declarations
int main(int argc, char *argv[]) {
void *v = make_vec(); // Use a real typedef here
add_number(v, 42);
print_vec(v);
drop_vec(v);
}
You'd want to run this under valgrind to make sure I didn't do anything stupid memory-wise.

Rust FFI. Casting to void pointer

I've a function which has prototype as below
//opaque struct
struct mosquitto;
struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *obj);
In my c code, I'm calling it as below.
struct mosquitto *m = mosquitto_new(buf, true, NULL);
Now I want to call the above API in my rust code. rust-bindgen generated the following bindings
pub enum Struct_mosquitto { }
pub fn mosquitto_new(id: *const ::libc::c_char, clean_session: u8, obj: *mut ::libc::c_void) -> *mut Struct_mosquitto;
When I'm trying to call the above API, I'm getting a mismatch at 3rd argument.
let s = CString::new("ravi").unwrap();
let mqtt = mosquitto::mosquitto_new(s.as_ptr(), 1, ptr::null());
How do I pass NULL to *mut c_void?
BONUS QUESTION: How to pass a rust struct to *mut c_void ?
The ptr::null() function returns a *const T, what you want is the ptr::null_mut() function, since the argument to your function is of type *mut ::libc::c_void.
For passing an actual value, have a look at the answer to Working with c_void in an FFI

Resources