Skip to content

FFI call to C adds an extra argument before the specified argument list #7627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kevinmehall opened this issue Jul 6, 2013 · 2 comments
Closed

Comments

@kevinmehall
Copy link
Contributor

I'm trying to call a function in a C library with two args, a pointer and an int, and was seeing the C function segfault. When I break on the C function in GDB, I see that the pointer I passed in the first argument from Rust shows up in the second argument, and there's another address added as the first argument.

Simplified to a minimal example by passing a constant for the pointer to show that it's shifted into the second argument:

libusb_set_debug(10 as *mut libusb_context, 4);
/* Breakpoint 1, libusb_set_debug (ctx=0x804e738, level=10) at core.c:1600 */

I'm using Fedora 18, 32-bit
rustc 0.7 (3e933b1 2013-07-06 08:32:10 -0700)
host: i686-unknown-linux-gnu

Here's the original code and GDB output:

use std::cast;
use std::libc::{c_void, c_int, size_t, malloc, free};
use std::ptr;
use std::unstable::intrinsics;

struct libusb_context;

#[link_args = "-lusb-1.0"]
extern{
    fn libusb_init(ctx: **mut libusb_context) -> c_int;
    fn libusb_exit(ctx: *mut libusb_context) -> c_void;
    fn libusb_set_debug(ctx: *mut libusb_context, level: c_int) -> c_void;
}

fn main() {
    unsafe{
        let mut ctx: *mut libusb_context = intrinsics::init();
        let r = libusb_init(&ctx);
        println(fmt!("r=%?, ctx=0x%x", r, ctx as uint));
        libusb_set_debug(ctx, 4);
        println("set debug");
    }
}

/*
r=0, ctx=0xb7a00468
[Switching to Thread 0xb7bffb40 (LWP 32527)]

Breakpoint 1, libusb_set_debug (ctx=0x805b690, level=-1214249880)
    at core.c:1600
1600    {
(gdb) p/x level
$1 = 0xb7a00468
*/

I also tried calling libusb_exit(ctx), and it also receives the wrong pointer and segfaults.

@jdm
Copy link
Contributor

jdm commented Jul 7, 2013

You probably need to use extern "C" instead of plain extern.

@kevinmehall
Copy link
Contributor Author

I had the functions returning c_void, thinking that mapped to C functions that return void. However, c_void is just an empty enum in libc.rs:

pub enum c_void {}

It looks like Rust enums are passed as C structs. The cdecl calling convention specifies that to return a structure, the caller passes a hidden first argument. I fixed it by removing the return type from my extern fn:

fn libusb_set_debug(ctx: *mut libusb_context, level: c_int);

I suppose c_void is only intended to be used for void pointers.

bors added a commit that referenced this issue Jul 11, 2013
I added documentation for when to use and not to use `c_void`, since it tripped me up when I started. (See issue #7627)
flip1995 pushed a commit to flip1995/rust that referenced this issue Sep 8, 2021
…-again, r=llogiq

Updating issue templates again for rustbot

It turns out that our current issue template can sometimes trigger a rustbot error message, as can be seen in [rust-lang#7626](rust-lang/rust-clippy#7626). I originally tested this in rust-lang#7599, but it's apparently a bit inconsistent. This PR adds backticks to the commands, as correctly suggested by `@mikerite` in the comments. (Thank you!)

``@rustbot` label +S-blocked`

---

Now I also pushed a tiny link fix as well. 🙃

---

changelog: none
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants