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.

+3


source to share


1 answer


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

      

+5


source







All Articles