0

I'm calling a Rust function from C but the pointer address is changed when returned.

#[no_mangle]
pub extern fn plugin_get_config_string(config: *const toml::Value, k: *const c_char) -> *const c_char {
    let a = CString::new("dwadwad").unwrap();
    let p = a.as_ptr();
    println!("{:?}", p);
    mem::forget(a);
    p
}   
__declspec(dllexport) void initialize(void *config) {
    char *dwad = plugin_get_config_string(config, "host");
    printf("%p\n", dwad);
}

In Rust: 0x220d3ceee30

In C: FFFFFFFFD3CEEE30

The first 3 bytes are always stripped in C.

I'm using Visual Studio 2017 x64 Dev Command Prompt and stable-x86_64-pc-windows-msvc.

Anyone know what the problem might be?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Trl
  • 147
  • 2
  • 12
  • I would say, it is not a good idea to use a local (on stack allocated) variable or locally (in function) allocated heap value as return value. In the first case, the value will be destroyed after leaving the function, in second case you will have memory leaks. Unfortunately I do not understand Rust and I'm not sure what is the real behaviour of the code. But I can tell *(when it is similar to MFC/C++)*, that by destroying the `a`, you will also destroy it's value, which is stored in `p`. – Julo Dec 08 '18 at 05:25
  • 2
    You should use [`CString::into_raw`](https://doc.rust-lang.org/stable/std/ffi/struct.CString.html#method.into_raw) instead of `as_ptr` + `mem::forget`. Both methods are roughly equivalent, but `into_raw` is more idiomatic. – Francis Gagné Dec 08 '18 at 05:28

1 Answers1

3

In your C code, there's no prototype for plugin_get_config_string. The compiler therefore infers that the return type is int. When that int is then assigned to a char * variable, the int is sign-extended because a char * is larger than an int on your target platform.

The same reasoning applies to the function's parameters: they are implicitly defined as int, so your pointers coming from C will be truncated, and the arguments might be in the wrong place because of the size mismatch.

To fix this, you must declare a prototype like this:

const char *plugin_get_config_string(const void *config, const char *k);
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • `The compiler therefore infers that the return type is int .. The same reasoning applies to the function's parameters: they are implicitly defined as int` This is not true since C99, compile in pedantic C99/C11/C(C17) will result in a error. https://rextester.com/DVEH7616, https://stackoverflow.com/a/437763/7076153. However, many compiler don't follow this rule by default to be retro compatible, this is stupid, but they do what they want. – Stargateur Dec 08 '18 at 07:21
  • Also `const char *plugin_get_config_string(void *config, const char *k);` `config` should also be mark as `const` and `extern fn plugin_get_config_string(config: *const toml::Value, k: *const c_char) -> *const c_char` is incorrect `config` should be of type `*const c_void` and the Rust side should cast it. – Stargateur Dec 08 '18 at 07:29