Here's some code I generated using c2rust and then cleaned up a bit:
#![feature(libc)]
extern crate libc;
use libc::*;
use std::mem::transmute;
extern "C" {
#[no_mangle]
fn read(__fd: c_int, __buf: *mut c_void, __nbytes: c_ulong) -> c_long;
#[no_mangle]
fn mmap(
__addr: *mut c_void,
__len: c_ulong,
__prot: c_int,
__flags: c_int,
__fd: c_int,
__offset: c_long,
) -> *mut c_void;
}
pub fn main() {
unsafe {
let buf: *mut c_void = mmap(
0 as *mut c_void,
256i32 as c_ulong,
0x1i32 | 0x2i32 | 0x4i32,
0x2i32 | 0x20i32,
-1i32,
0i32 as c_long,
);
read(0i32, buf, 256i32 as c_ulong);
transmute::<*mut c_void, Option<unsafe extern "C" fn() -> ()>>(buf).unwrap()();
}
}
While I understand what it does, I'm not sure how to interpret the last expression. What does Option<unsafe extern "C" fn() -> ()> mean?
We're trying to call an unsafe extern "C" fn() -> (), which is basically a function with no arguments and no return type. My first attempt was to just use the as keyword, as defined in transmute's documentation. I got the following error:
error[E0605]: non-primitive cast: `*mut libc::c_void` as `unsafe extern "C" fn()`
--> wx.rs:32:9
|
32 | (buf as unsafe extern "C" fn() -> ())();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait
It looks like the function is a non-primitive type and this is why I need transmute. I tried the following:
transmute::<
*mut c_void,
unsafe extern "C" fn() -> ()
>(buf)();
And the code compiled and actually ran as expected.
What I still don't understand is why Option was used by c2rust, but the code works fine without it. It appears that unsafe and extern "C" can also be dropped and the code still works, at least for me.
Related
I need a global static variable, that references a extern "C" function.
I get the following compiler error:
error[E0308]: mismatched types
--> src/main.rs:17:28
|
17 | static BAR: Bar = Bar::new(&foobar);
| ^^^^^^^ expected fn pointer, found fn item
|
= note: expected reference `&'static unsafe extern "C" fn() -> !`
found reference `&unsafe extern "C" fn() -> ! {foobar}`
My code down below or on Rust playground
extern "C" {
fn foobar() -> !;
}
struct Bar {
foo: &'static unsafe extern "C" fn() -> !
}
impl Bar {
const fn new(foo: &'static unsafe extern "C" fn() -> !) -> Self {
Self {
foo
}
}
}
static BAR: Bar = Bar::new(&foobar);
fn main() {
}
How can I solve this?
The fn type is already a pointer (called a "function pointer"), so you don't need to put it behind a reference:
struct Bar {
foo: unsafe extern "C" fn() -> !,
}
It can be created like this:
impl Bar {
const fn new(foo: unsafe extern "C" fn() -> !) -> Self {
Self {
foo
}
}
}
static BAR: Bar = Bar::new(foobar);
When you try to compile this, Rust tells you that function pointers in const contexts are unstable. By using the Nightly channel and enabling the const_fn_fn_ptr_basics feature, it works:
Playground
(This is likely to change in the future, don't hesitate to update this answer when needed)
I am following up on Casting a borrowed reference with a lifetime to a raw pointer in Rust, which solved the wrong problem.
Please consider the following code:
extern crate jni;
extern crate ffi;
use jni::JNIEnv;
use jni::objects::JClass;
use jni::sys::{jint, jlong, jobject};
struct CameraAppEngine {
_env: *mut jni::sys::JNIEnv,
_width: i32,
_height: i32
}
impl CameraAppEngine {
pub fn new(_env: *mut jni::sys::JNIEnv, _width: i32, _height: i32) -> CameraAppEngine {
CameraAppEngine { _env, _width, _height }
}
pub fn create_camera_session(&mut self, surface: jobject) {
// error!
let window = ffi::ANativeWindow_fromSurface(self._env, surface);
}
}
fn app_engine_create(env: &JNIEnv, width: i32, height: i32) -> *mut CameraAppEngine {
let engine = CameraAppEngine::new(env.get_native_interface(), width, height);
Box::into_raw(Box::new(engine))
}
#[no_mangle]
pub extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_createCamera(env: JNIEnv<'static>, _: JClass, width:jint, height:jint) -> jlong {
app_engine_create(&env, width, height) as jlong
}
#[no_mangle]
pub extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_onPreviewSurfaceCreated(_: JNIEnv, _: JClass, engine_ptr:jlong, surface:jobject) {
let mut app = unsafe { Box::from_raw(engine_ptr as *mut CameraAppEngine) };
app.create_camera_session(surface);
}
And in the ffi crate we have:
extern "C" {
pub fn ANativeWindow_fromSurface(env: *mut JNIEnv, surface: jobject) -> *mut ANativeWindow;
}
This results in:
error[E0308]: mismatched types
--> native_app/src/lib.rs:24:53
|
| let window = ffi::ANativeWindow_fromSurface(self._env, surface);
| ^^^^^^^^^ expected struct `ffi::JNINativeInterface`, found struct `jni::sys::JNINativeInterface_`
|
= note: expected raw pointer `*mut *const ffi::JNINativeInterface`
found raw pointer `*mut *const jni::sys::JNINativeInterface_`
error[E0308]: mismatched types
--> native_app/src/lib.rs:24:64
|
| let window = ffi::ANativeWindow_fromSurface(self._env, surface);
| ^^^^^^^ expected enum `std::ffi::c_void`, found enum `jni::sys::_jobject`
|
= note: expected raw pointer `*mut std::ffi::c_void`
found raw pointer `*mut jni::sys::_jobject`
The problem is that the JNIEnv type expected by ANativeWindow_fromSurface is actually unrelated to jni::sys::JNIEnv entirely.
It's defined in ffi like so:
pub type JNIEnv = *const JNINativeInterface;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct JNINativeInterface {
pub reserved0: *mut ::std::os::raw::c_void,
pub reserved1: *mut ::std::os::raw::c_void,
pub reserved2: *mut ::std::os::raw::c_void,
pub reserved3: *mut ::std::os::raw::c_void,
pub GetVersion: ::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv) -> jint>,
pub DefineClass: ::std::option::Option<
unsafe extern "C" fn(
arg1: *mut JNIEnv,
arg2: *const ::std::os::raw::c_char,
arg3: jobject,
arg4: *const jbyte,
arg5: jsize,
) -> jclass,
>,
pub FindClass: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: *const ::std::os::raw::c_char) -> jclass,
>,
pub FromReflectedMethod:
::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: jobject) -> jmethodID>,
pub FromReflectedField:
::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: jobject) -> jfieldID>,
pub ToReflectedMethod: ::std::option::Option<
unsafe extern "C" fn(
arg1: *mut JNIEnv,
arg2: jclass,
arg3: jmethodID,
arg4: jboolean,
) -> jobject,
>
// etc...
}
Given the glue code shown in the example, how do I get a valid reference to ffi::JNIEnv so that I may pass it to the ANativeWindow_fromSurface method. Bonus points if you give advice on converting jni::sys::jobject to *mut std::os::raw::c_void (lifetime concerns, null pointers, etc.).
Full source to ffi definitions
Here is the basic helloworld implementation I used to prove the concept in the accepted answer:
use std::ffi::{CString, CStr};
use std::os::raw::{c_char};
/// Expose the JNI interface for android below
#[cfg(target_os="android")]
#[allow(non_snake_case)]
pub mod android {
extern crate ffi;
use super::*;
use self::ffi::{JNIEnv, jclass, jstring, jlong};
#[derive(Debug)]
struct AppEngine {
greeting: *mut c_char
}
unsafe fn rust_greeting(app: *mut AppEngine) -> *mut c_char {
let app = Box::from_raw(app);
app.greeting
}
/// Constructs an AppEngine object.
fn rust_engine_create(to: *const c_char) -> *mut AppEngine {
let c_str = unsafe { CStr::from_ptr(to) };
let recipient = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let greeting = CString::new("Hello ".to_owned() + recipient).unwrap().into_raw();
let app = AppEngine{greeting: greeting};
Box::into_raw(Box::new(app))
}
/// Destroys an AppEngine object previously constructed using `rust_engine_create()`.
unsafe fn rust_engine_destroy(app: *mut AppEngine) {
drop(Box::from_raw(app))
}
#[no_mangle]
pub unsafe extern fn Java_io_waweb_cartoonifyit_MainActivity_greeting(env: &mut JNIEnv, _: jclass, app_ptr: jlong) -> jstring {
let app = app_ptr as *mut AppEngine;
let new_string = env.as_ref().unwrap().NewStringUTF.unwrap();
new_string(env, rust_greeting(app))
}
#[no_mangle]
pub unsafe extern fn Java_io_waweb_cartoonifyit_MainActivity_createNativeApp(env: &mut JNIEnv, _: jclass, java_pattern: jstring) -> jlong {
let get_string_chars = env.as_ref().unwrap().GetStringChars.unwrap();
let is_copy = 0 as *mut u8;
rust_engine_create(get_string_chars(env, java_pattern, is_copy) as *const c_char ) as jlong
}
#[no_mangle]
pub unsafe extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_destroyNativeApp(_: JNIEnv, _: jclass, app_ptr: jlong) {
let app = app_ptr as *mut AppEngine;
rust_engine_destroy(app)
}
}
This is just a proof of concept. More care should be taken when casting raw pointers about. Also see notes about Box::leak in the accepted answer.
A JNIEnv is a pointer to a struct used for communication between Java and native code. This communication ABI is implemented by pretty much every JVM(and android). There are multiple versions of the aforementioned structs, which is what the GetVersion field is for.
It seems to me that you are using an external jni crate along with your own ffi crate generated from this wrapper. I would expect your ffi crate to be the most correct since it is using android headers, instead of the standard JVM headers which a most likely being used by the jni crate.
One last note Box::from_raw(engine_ptr as *mut CameraAppEngine), creates a box which will free the memory located at engine_ptr. This is likely not what you want. Consider using Box::leak to leak the created Box and avoid use after frees.
This is how the C API looks
void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int));
rust-bindgen has generated this for me
pub fn mosquitto_connect_callback_set(
mosq: *mut Struct_mosquitto,
on_connect: ::std::option::Option<
extern "C" fn(
arg1: *mut Struct_mosquitto,
arg2: *mut ::libc::c_void,
arg3: ::libc::c_int,
) -> (),
>,
)
How do I create a Rust callback function to pass to the on_connect parameter in the above Rust binding?
The Rust Programming Language, first edition, has a section about FFI titled Callbacks from C code to Rust functions.
The example from there is
extern "C" fn callback(a: i32) {
println!("I'm called from C with value {0}", a);
}
#[link(name = "extlib")]
extern "C" {
fn register_callback(cb: extern "C" fn(i32)) -> i32;
fn trigger_callback();
}
fn main() {
unsafe {
register_callback(callback);
trigger_callback(); // Triggers the callback
}
}
For your specific case, you already know the specific type of function you need:
extern "C" fn mycallback(
arg1: *mut Struct_mosquitto,
arg2: *mut ::libc::c_void,
arg3: ::libc::c_int,
) -> () {
println!("I'm in Rust!");
}
And then use it like
mosquitto_connect_callback_set(mosq, Some(mycallback));
I want to point pthread_create to a C function I later link to. That C function will use pthread_cleanup_push and pthread_cleanup_pop which are C macros and thus cannot be ported to Rust.
This is my code:
extern crate libc;
use std::ptr::null_mut;
use libc::c_void;
extern "C" {
fn thr_fn1(arg:*mut c_void) -> *mut c_void;
}
fn main() {
let mut tid1 = std::mem::zeroed();
libc::pthread_create(&mut tid1, null_mut(), thr_fn1, null_mut());
}
I expected that since I'm calling libc's FFI anyway, I can just point to an external C function, but I get an error:
error[E0308]: mismatched types
--> src/bin/11-threads/f05-thread-cleanup.rs:25:49
|
25 | libc::pthread_create(&mut tid1, null_mut(), thr_fn1, null_mut());
| ^^^^^^^ expected normal fn, found unsafe fn
|
= note: expected type `extern "C" fn(*mut libc::c_void) -> *mut libc::c_void`
found type `unsafe extern "C" fn(*mut libc::c_void) -> *mut libc::c_void {thr_fn1}`
I could write a wrapper which calls the C function in an unsafe{} block, but is there any way to avoid that?
The libc function definition is wrong: The C header /usr/include/pthread.h:
extern int pthread_create (pthread_t *__restrict __newthread,
const pthread_attr_t *__restrict __attr,
void *(*__start_routine) (void *),
void *__restrict __arg) __THROWNL __nonnull ((1, 3));
bindgen produces this function definition:
pub fn pthread_create(arg1: *mut pthread_t,
arg2: *const pthread_attr_t,
arg3: Option<unsafe extern "C" fn(arg1: *mut c_void) -> *mut c_void>,
arg4: *mut c_void) -> c_int;
Which compiles both on macOS and Linux. I'm not sure Option is a good idea here; why would anyone start a thread which doesn't call a function?
I opened a PR for libc to correct the issue (without the Option part)
This is how the C API looks
void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int));
rust-bindgen has generated this for me
pub fn mosquitto_connect_callback_set(
mosq: *mut Struct_mosquitto,
on_connect: ::std::option::Option<
extern "C" fn(
arg1: *mut Struct_mosquitto,
arg2: *mut ::libc::c_void,
arg3: ::libc::c_int,
) -> (),
>,
)
How do I create a Rust callback function to pass to the on_connect parameter in the above Rust binding?
The Rust Programming Language, first edition, has a section about FFI titled Callbacks from C code to Rust functions.
The example from there is
extern "C" fn callback(a: i32) {
println!("I'm called from C with value {0}", a);
}
#[link(name = "extlib")]
extern "C" {
fn register_callback(cb: extern "C" fn(i32)) -> i32;
fn trigger_callback();
}
fn main() {
unsafe {
register_callback(callback);
trigger_callback(); // Triggers the callback
}
}
For your specific case, you already know the specific type of function you need:
extern "C" fn mycallback(
arg1: *mut Struct_mosquitto,
arg2: *mut ::libc::c_void,
arg3: ::libc::c_int,
) -> () {
println!("I'm in Rust!");
}
And then use it like
mosquitto_connect_callback_set(mosq, Some(mycallback));