How do I initialize an opaque C struct when using Rust FFI? - rust

Here's what I would like to do in C code:
#include <some_lib.h>
int main() {
some_lib_struct_t x;
some_lib_func(&x);
}
How do I make use of the library in Rust? Here's what I've got so far:
extern crate libc; // 0.2.51
struct some_lib_struct_t;
#[link(name = "some_lib")]
extern "C" {
fn some_lib_func(x: *mut some_lib_struct_t);
}
fn main() {
let mut x: some_lib_struct_t;
unsafe {
some_lib_func(&mut x);
}
}
When compiling I get an error:
error[E0381]: borrow of possibly uninitialized variable: `x`
--> src/main.rs:13:23
|
13 | some_lib_func(&mut x);
| ^^^^^^ use of possibly uninitialized `x`

The safest answer is to initialize the struct yourself:
let mut x: some_lib_struct_t = some_lib_struct_t;
unsafe {
some_lib_func(&mut x);
}
The closest analog to the C code is to use MaybeUninit
use std::mem::MaybeUninit;
unsafe {
let mut x = MaybeUninit::uninit();
some_lib_func(x.as_mut_ptr());
}
Before Rust 1.36, you can use mem::uninitialized:
unsafe {
let mut x: some_lib_struct_t = std::mem::uninitialized();
some_lib_func(&mut x);
}
You have to be sure that some_lib_func completely initializes all the members of the struct, otherwise the unsafety will leak outside of the unsafe block.
Speaking of "members of the struct", I can almost guarantee your code won't do what you want. You've defined some_lib_struct_t as having zero size. That means that no stack space will be allocated for it, and a reference to it won't be what your C code is expecting.
You need to mirror the definition of the C struct in Rust so that the appropriate size, padding, and alignment can be allocated. Usually, this means using repr(C).
Many times, C libraries avoid exposing their internal struct representation by always returning a pointer to the opaque type:
What's the Rust idiom to define a field pointing to a C opaque pointer?
In Rust how can I define or import a C struct from a third party library?
Allocating an object for C / FFI library calls

From the documentation for mem::uninitialized():
Deprecated since 1.39.0: use mem::MaybeUninit instead
The new solution would look like this:
use std::mem::MaybeUninit;
let instance = unsafe {
let mut x: MaybeUninit<some_lib_struct_t> = MaybeUninit::uninit();
some_lib_func(x.as_mut_ptr());
x.assume_init()
}

After reading Shepmaster's answer, I looked closer at the header for the library. Just as they said, some_lib_struct_t was just a typedef for a pointer to actual_lib_struct_t. I made the following changes:
extern crate libc;
struct actual_lib_struct_t;
type some_lib_type_t = *mut actual_lib_struct_t;
#[link(name="some_lib")]
extern {
fn some_lib_func(x: *mut some_lib_type_t);
}
fn main() {
let mut x: some_lib_type_t;
unsafe {
x = std::mem::uninitialized();
some_lib_func(&mut x);
}
}
And it works! I do however get the warning found zero-size struct in foreign module, consider adding a member to this struct, #[warn(improper_ctypes)] on by default.

Related

Instantiating a struct that has private fields [duplicate]

I'm trying to learn more about FFI in Rust and linking with C libraries, specifically libc. While on my "quest", I came across the following problem.
Normal pattern in C
void(* sig_set(int sig, void(*handler)(int))) {
// uninitialized sigaction structs
struct sigaction new_action, old_action;
// assign options to new action
new_action.sa_flags = SA_RESTART;
new_action.sa_handler = handler;
sigemptyset(&new_action.sa_mask);
if(sigaction(sig, &new_action, &old_action) < 0) {
fprintf(stderr, "Error: %s!\n", "signal error");
exit(1);
}
return old_action.sa_handler;
}
Attempt in Rust
use libc; // 0.2.77
fn sig_init(sig: i32, handler: fn(i32) -> ()) -> usize {
unsafe {
let mut new_action: libc::sigaction;
let mut old_action: libc::sigaction;
new_action.sa_flags = 0x10000000;
new_action.sa_sigaction = handler as usize;
libc::sigemptyset(&mut new_action.sa_mask as *mut libc::sigset_t);
libc::sigaction(
sig,
&mut new_action as *mut libc::sigaction,
&mut old_action as *mut libc::sigaction,
);
old_action.sa_sigaction
}
}
The compiler will throw the following error:
error[E0381]: assign to part of possibly-uninitialized variable: `new_action`
--> src/lib.rs:8:9
|
8 | new_action.sa_flags = 0x10000000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `new_action`
error[E0381]: borrow of possibly-uninitialized variable: `old_action`
--> src/lib.rs:15:13
|
15 | &mut old_action as *mut libc::sigaction,
| ^^^^^^^^^^^^^^^ use of possibly-uninitialized `old_action`
This makes sense as very bad things could happen if sigemptyset were to read from sa_mask. So I tried the following on line 3 of the above.
let mut new_action: libc::sigaction = libc::sigaction {
sa_sigaction: handler as usize,
sa_flags: 0x10000000,
sa_mask: mask,
};
This will not work as _restorer is missing in the above example, but _restorer is private. How would I get around this problem or a similar situation? Would you use something like mem::transmute?
The standard library defines a couple of types and functions to deal with initialization. They are generic, so they can be used to initialize values of any type.
Modern Rust suggests using MaybeUninit in most cases. Applied to your case, it would look something like:
use std::mem::MaybeUninit;
let mut new_action: libc::sigaction = MaybeUninit::zeroed().assume_init();
let mut old_action: libc::sigaction = MaybeUninit::zeroed().assume_init();
MaybeUninit was stabilized in Rust 1.36. Before that, you could use std::mem::uninitialized(), which gives you an uninitialized value. LLVM will consider the contents to be undefined, and will perform aggressive optimizations based on this. You must initialize any value before it's read.
More appropriate to your case, there's std::mem::zeroed(), which gives you a value whose storage is filled with zeroes. This function is unsafe because such a value is not necessarily legal for all types. zeroed() is appropriate for "plain old data" (POD) types. Applied to your case, it would look something like:
use std::mem;
let mut new_action: libc::sigaction = mem::zeroed();
let mut old_action: libc::sigaction = mem::zeroed();
How about using mem::zeroed? The docs even say:
This is useful for FFI functions sometimes, but should generally be avoided.

How to wrap a call to a FFI function that uses VarArgs in Rust?

mexPrintf, just like printf, accepts a varargs list of arguments, but I don't know what the best way to wrap this is in Rust. There is a RFC for variadic generics, but what can we do today?
In this example, I want to print of the number of inputs and outputs, but the wrapped function just prints garbage. Any idea how to fix this?
#![allow(non_snake_case)]
#![allow(unused_variables)]
extern crate mex_sys;
use mex_sys::mxArray;
use std::ffi::CString;
use std::os::raw::c_int;
use std::os::raw::c_void;
type VarArgs = *mut c_void;
// attempt to wrap mex_sys::mexPrintf
fn mexPrintf(fmt: &str, args: VarArgs) {
let cs = CString::new(fmt).unwrap();
unsafe {
mex_sys::mexPrintf(cs.as_ptr(), args);
}
}
#[no_mangle]
pub extern "system" fn mexFunction(
nlhs: c_int,
plhs: *mut *mut mxArray,
nrhs: c_int,
prhs: *mut *mut mxArray,
) {
let hw = CString::new("hello world\n").unwrap();
unsafe {
mex_sys::mexPrintf(hw.as_ptr());
}
let inout = CString::new("%d inputs and %d outputs\n").unwrap();
unsafe {
mex_sys::mexPrintf(inout.as_ptr(), nrhs, nlhs);
}
mexPrintf("hello world wrapped\n", std::ptr::null_mut());
let n = Box::new(nrhs);
let p = Box::into_raw(n);
mexPrintf("inputs %d\n", p as VarArgs);
let mut v = vec![3];
mexPrintf("vec %d\n", v.as_mut_ptr() as VarArgs);
}
Contrary to popular belief, it is possible to call variadic / vararg functions that were defined in C. That doesn't mean that doing so is very easy, and it's definitely even easier to do something bad because there are even fewer types for the compiler to check your work with.
Here's an example of calling printf. I've hard-coded just about everything:
extern crate libc;
fn my_thing() {
unsafe {
libc::printf(b"Hello, %s (%d)\0".as_ptr() as *const i8, b"world\0".as_ptr(), 42i32);
}
}
fn main() {
my_thing()
}
Note that I have to very explicitly make sure my format string and arguments are all the right types and the strings are NUL-terminated.
Normally, you'll use tools like CString:
extern crate libc;
use std::ffi::CString;
fn my_thing(name: &str, number: i32) {
let fmt = CString::new("Hello, %s (%d)").expect("Invalid format string");
let name = CString::new(name).expect("Invalid name");
unsafe {
libc::printf(fmt.as_ptr(), name.as_ptr(), number);
}
}
fn main() {
my_thing("world", 42)
}
The Rust compiler test suite also has an example of calling a variadic function.
A word of warning specifically for printf-like functions: C compiler-writers realized that people screw up this particular type of variadic function call all the time. To help combat that, they've encoded special logic that parses the format string and attempts to check the argument types against the types the format string expect. The Rust compiler will not check your C-style format strings for you!
I had confused a "variable list of arguments" with a va_list. I'm going to avoid both if I can and in this situation, I'm just going to do the string formatting in Rust before passing it to interop. Here is what worked for me in this case:
#![allow(non_snake_case)]
#![allow(unused_variables)]
extern crate mex_sys;
use mex_sys::mxArray;
use std::ffi::CString;
use std::os::raw::c_int;
// attempt to wrap mex_sys::mexPrintf
fn mexPrintf(text: &str) {
let cs = CString::new(text).expect("Invalid text");
unsafe {
mex_sys::mexPrintf(cs.as_ptr());
}
}
#[no_mangle]
pub extern "C" fn mexFunction(
nlhs: c_int,
plhs: *mut *mut mxArray,
nrhs: c_int,
prhs: *mut *mut mxArray,
) {
mexPrintf(&format!("{} inputs and {} outputs\n", nrhs, nlhs));
}

How would you go about creating a pointer to a specific memory address in Rust?

For example, let's say I want to access whatever value is stored at 0x0900. I found the function std::ptr::read in the Rust standard library, but the documentation isn't super clear on how to use it and I'm not sure if it's the right way.
This is what I've tried:
use std::ptr;
fn main() {
let n = ptr::read("0x0900");
println!("{}", n);
}
but it gives me error E0277
If you want to read a value of type u32 from memory location 0x0900, you could do it as follows:
use std::ptr;
fn main() {
let p = 0x0900 as *const u32;
let n = unsafe { ptr::read(p) };
println!("{}", n);
}
Note that you need to decide what type of pointer you want when casting the address to a pointer.

How can I initialize sigset_t or other variables used as "out parameters" in Rust?

I'm trying to learn more about FFI in Rust and linking with C libraries, specifically libc. While on my "quest", I came across the following problem.
Normal pattern in C
void(* sig_set(int sig, void(*handler)(int))) {
// uninitialized sigaction structs
struct sigaction new_action, old_action;
// assign options to new action
new_action.sa_flags = SA_RESTART;
new_action.sa_handler = handler;
sigemptyset(&new_action.sa_mask);
if(sigaction(sig, &new_action, &old_action) < 0) {
fprintf(stderr, "Error: %s!\n", "signal error");
exit(1);
}
return old_action.sa_handler;
}
Attempt in Rust
use libc; // 0.2.77
fn sig_init(sig: i32, handler: fn(i32) -> ()) -> usize {
unsafe {
let mut new_action: libc::sigaction;
let mut old_action: libc::sigaction;
new_action.sa_flags = 0x10000000;
new_action.sa_sigaction = handler as usize;
libc::sigemptyset(&mut new_action.sa_mask as *mut libc::sigset_t);
libc::sigaction(
sig,
&mut new_action as *mut libc::sigaction,
&mut old_action as *mut libc::sigaction,
);
old_action.sa_sigaction
}
}
The compiler will throw the following error:
error[E0381]: assign to part of possibly-uninitialized variable: `new_action`
--> src/lib.rs:8:9
|
8 | new_action.sa_flags = 0x10000000;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of possibly-uninitialized `new_action`
error[E0381]: borrow of possibly-uninitialized variable: `old_action`
--> src/lib.rs:15:13
|
15 | &mut old_action as *mut libc::sigaction,
| ^^^^^^^^^^^^^^^ use of possibly-uninitialized `old_action`
This makes sense as very bad things could happen if sigemptyset were to read from sa_mask. So I tried the following on line 3 of the above.
let mut new_action: libc::sigaction = libc::sigaction {
sa_sigaction: handler as usize,
sa_flags: 0x10000000,
sa_mask: mask,
};
This will not work as _restorer is missing in the above example, but _restorer is private. How would I get around this problem or a similar situation? Would you use something like mem::transmute?
The standard library defines a couple of types and functions to deal with initialization. They are generic, so they can be used to initialize values of any type.
Modern Rust suggests using MaybeUninit in most cases. Applied to your case, it would look something like:
use std::mem::MaybeUninit;
let mut new_action: libc::sigaction = MaybeUninit::zeroed().assume_init();
let mut old_action: libc::sigaction = MaybeUninit::zeroed().assume_init();
MaybeUninit was stabilized in Rust 1.36. Before that, you could use std::mem::uninitialized(), which gives you an uninitialized value. LLVM will consider the contents to be undefined, and will perform aggressive optimizations based on this. You must initialize any value before it's read.
More appropriate to your case, there's std::mem::zeroed(), which gives you a value whose storage is filled with zeroes. This function is unsafe because such a value is not necessarily legal for all types. zeroed() is appropriate for "plain old data" (POD) types. Applied to your case, it would look something like:
use std::mem;
let mut new_action: libc::sigaction = mem::zeroed();
let mut old_action: libc::sigaction = mem::zeroed();
How about using mem::zeroed? The docs even say:
This is useful for FFI functions sometimes, but should generally be avoided.

In Rust how can I define or import a C struct from a third party library?

The Rust FFI guide (http://static.rust-lang.org/doc/master/guide-ffi.html) nicely demonstrates how to import C functions that use standard C types that are wrapped by the Rust std::lib library, such as size_t. But how do I bring C data structures defined in third party libraries into a Rust program?
I'm working with libmemcached, which has the following function:
memcached_st* memcached_create(memcached_st *ptr)
which is typically invoked like so in a C program (to kick things off):
#include <libmemcached/memcached.h>
// ...
memcached_st *memc;
memc = memcached_create(NULL);
The memcached_st is an opaque C struct - how do I declare and use that in a Rust program? Here are my failed attempts so far:
use std::libc::*;
use ptr;
#[link(name = "memcached")]
extern {
struct memcached_st; // error: unexpected token: `struct`
memcached_st* memcached_create(memcached_st *ptr);
}
fn main() {
unsafe {
let memc = memcached_create(ptr:null());
println!("{:?}", memc);
}
}
and
use std::libc::*;
use ptr;
#[link(name = "memcached")]
extern {
// error: unexpected token: `memcached_st`
memcached_st* memcached_create(memcached_st *ptr);
}
fn main() {
unsafe {
let memc = memcached_create(ptr:null());
println!("{:?}", memc);
}
}
Using empty structure is a valid approach. You almost got it, you just don't need to put the struct definition in extern block. Also you can't use C code inside Rust sources - extern definitions have to follow standard Rust syntax.
use std::ptr;
struct memcached_st;
#[link(name = "memcached")]
extern {
fn memcached_create(ptr: *memcached_st) -> *memcached_st;
}
fn main() {
unsafe {
let memc = memcached_create(ptr::null());
println!("{:?}", memc);
}
}

Resources