Skip to content

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

Description

@kevinmehall

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.

Activity

  1. jdm commented on Jul 7, 2013

    @jdm
    Contributor

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

  2. kevinmehall commented on Jul 7, 2013

    @kevinmehall
    ContributorAuthor

    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.

  3. added a commit that references this issue on Jul 11, 2013
  4. added a commit that references this issue on Sep 8, 2021
  5. added a commit that references this issue on Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions