Error in FFI while passing CString followed by int
My Rust Test Code
extern "C" {
fn test_int_only(n : libc::c_int);
fn test_int_and_str(s : CString , n : libc::c_int);
}
pub fn test1() {
unsafe {
test_int_only(0);
test_int_only(1);
test_int_only(2);
test_int_only(4);
test_int_only(-12);
}
}
pub fn test2() {
unsafe {
test_int_and_str(CString::new("Foo").unwrap(),0);
test_int_and_str(CString::new("Bar").unwrap(),1);
test_int_and_str(CString::new("Baz").unwrap(),2);
test_int_and_str(CString::new("Fub").unwrap(),4);
test_int_and_str(CString::new("Bub").unwrap(),-12);
}
}
My C code
void test_int_only(int abc){
printf("%d\n", abc);
}
void test_int_and_str(const char* name,int abc) {
printf("%s %d\n", name, abc);
}
When testing for test_int_only ()
1
2
4
-12
When testing for test_int_and_str ()
Foo 4
Bar 4
Baz 4
Fub 4
Bub 4
It seems that the second argument is being interpreted (in both rust and in) as a sizeof string, not a value passed from the Rust code. I am guessing it has to do with either a call or a null termination which is not working correctly. It is a C dll, with a call to the cccdecl convention (windows 32bit dll). Interestingly, passing a (opaque) pointer and int (in another test) works great, so I don't think this is a calling problem.
source to share
It seems that the 2nd argument is being interpreted (in both rust and in) as a sizeof string, not a value passed from the Rust code.
Right. Here you are experiencing undefined behavior.
Your C function has a different signature from the extern function that you specified in Rust-Code. First of all, passing through types that are not #[repr(C)]
for extern function is undefined behavior, and for that afaik lint was used. Secondly, CString
it is not char*
, it is a structure with internal data. If you want to transfer const char*
, you need to transfer *const u8
. You can get such a pointer from CString
via a function into_ptr
.
Note that there is into_ptr
a memory leak, you need to use it again from_ptr
to get an object CString
that can be freed. If you just want to select an object CString
, you can do something along the following lines:
// ensure c_str lives longer than the function call
let c_str = CString::new("Foo").unwrap();
unsafe { test_int_and_str(c_str.as_ptr(), 0); }
// let the destructor of c_str free the memory
source to share