Why did my integer value change when passing a heap allocated structure from Rust to C?

I'm passing data from Rust to C. While traversing the primitives seems easy, I kind of get lost in the structures.

I have the following Rust code:

use ::std::os::raw::*;

static NAME: &[c_char] = &[65, 66, 67, 0];

#[repr(C)]
pub struct MyStruct {
    pub x: c_int,
    pub y: *const c_char,
}

#[no_mangle]
pub extern "C" fn get_my_struct() -> *const MyStruct {
    let my_struct = MyStruct {
        x: 11 as c_int,
        y: NAME.as_ptr(),
    };

    unsafe {
        ::std::mem::transmute(Box::new(my_struct))
    }
}

      

And the following C code:

typedef struct _my_struct my_struct;
extern const my_struct get_my_struct(void);

struct _my_struct {
    int x;
    const char *y;
};

int main(void) {
  my_struct my_complex_struct = get_my_struct();

  return 0;
}

      

The exit from gdb says:

(gdb) p my_complex_struct
$1 = {x = 6295568, y = 0x7ffff7bce1e0 <ref> "ABC"}

      

The line looks great, but int is definitely off. What am I missing here? Why is x 6295568 and not 11?

Compiled:

gcc (Debian 4.9.2-10) 4.9.2
rustc 1.20.0-nightly (8d22af87d 2017-07-22)
cargo 0.21.0-nightly (ffab51954 2017-07-18)

      

through:

cargo build
gcc --std=c11 -g -o main src/main.c /test/target/debug/libtest.so -L target/debug/

      

+3


source to share


1 answer


You have a problem because your ABI doesn't match. You are returning a pointer to the allocated structure, but your C code claims that the function returns the structure directly.

As shown in the Rust FFI Omnibus chapter on objects , you should use Box::into_raw

:

#[no_mangle]
pub extern "C" fn get_my_struct() -> *const MyStruct {
    let my_struct = MyStruct {
        x: 11 as c_int,
        y: NAME.as_ptr(),
    };

    Box::into_raw(Box::new(my_struct))
}

      

Your C function should be marked as returning a pointer:

extern const my_struct *get_my_struct(void);

// ...

int main(void) {
  const my_struct *my_complex_struct = get_my_struct();
  // ...
}

      

(lldb) p *my_complex_struct
(my_struct) $1 = (x = 11, y = "ABC")

      

The code also has a memory leak; you need to cast the pointer back to Rust so that it can be deallocated correctly.




If you'd like to return the structure directly, change your Rust code to not perform selection:

#[no_mangle]
pub extern "C" fn get_my_struct() -> MyStruct {
    MyStruct {
        x: 11 as c_int,
        y: NAME.as_ptr(),
    }
}

      

(lldb) p my_complex_struct
(my_struct) $0 = (x = 11, y = "ABC")

      


Disclaimer: I am the main contributor to Omnibus.

+6


source







All Articles