FFI: how to work with pointers


#1

Hi, I’m trying to build complete FFI bindings for Hyperic Sigar.

It does some memory management on it’s own for certain cases, such as lists of CPUs and so on, and unfortunately there are no examples of doing similar things / working with more compelx FFI structures are available (or at least I could find those).

They have a procedure that expects pointer:

SIGAR_DECLARE(int) sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist);

(here’s a C example for it: https://github.com/hyperic/sigar/blob/master/examples/cpuinfo.c)

Which, if I understand correctly translate to:

#[link(name = "sigar")]
extern {
    pub fn sigar_cpu_info_list_get(sigar: *mut sigar_t, cpu_info: *mut sigar_cpu_info_t) -> c_int;
}

As far as I understand, I should create a mutable null pointer and pass it to the fn:

let cpu_info: *mut sigar_cpu_info_t = std::ptr::null_mut();
sigar_cpu_info_list_get(sigar_ptr, cpu_info);

Although that somehow throws an “unknown error”. To be honest, I don’t even know which direction to dig.

Could anyone give a pointer?

Thank you.


#2

Look more carefully at the example code you linked. They define a sigar_cpu_list_t on the stack, then pass a pointer to that. &cpulist in Rust would be &mut cpulist. They do not define a pointer to sigar_cpu_list_t.


#3

I thought that you want to mention that the C function is called sigar_cpu_list_get but in Rust it is defined as sigar_cpu_info_list_get


#4

I tend to assume problems like that are just typoes or mis-copies; surely they double-checked the names first. Surely.


#5

I searched and there is a function of the same form.
Probably copied here by mistake the wrong function.


#6

Yes, sorry, I indeed mixed up two procedures while copying the code… Both of them actually are very similar other than that are for different types.
I’ve switched to default implementation as @DanielKeep suggested in order to keep the implementation as in the given example.

thank you lot for suggestion, extremely helpful!

Although I’m still struggling with the nested C structures, is there any documented way to reference a C array or maybe work with offsets?


#7

If you meant C structs(that’s what I understood), you might want to look into repr©.
Edit: I realized that might not help you if you didn’t see the repr© being used before, here is an example.

std::slice::from_raw_parts

std::pointer::offset


#8

thanks! Yes, I’m aware of repr(C), so far I’ve translated:

from

typedef struct {
    char vendor[128];
    char model[128];
    int mhz;
    int mhz_max;
    int mhz_min;
    sigar_uint64_t cache_size;
    int total_sockets;
    int total_cores;
    int cores_per_socket;
} sigar_cpu_info_t;

typedef struct {
    unsigned long number;
    unsigned long size;
    sigar_cpu_info_t *data;
} sigar_cpu_info_list_t;

to

#[repr(C)]
pub struct sigar_cpu_info_t {
    vendor:           [c_char; 128],
    model:            [c_char; 128],
    mhz:              c_int,
    mhz_max:          c_int,
    cache_size:       uint64_t,
    total_sockets:    c_int,
    total_cores:      c_int,
    cores_per_socket: c_int
}

#[repr(C)]
#[derive(Debug)]
pub struct sigar_cpu_info_list_t {
    number: c_int,
    size: c_int,
    data: *mut sigar_cpu_info_t
}

I’m reading through the offset docs, so far the int fields (number and size) are working fine, although data is always 0x8 so I can’t seem to deref. Will read the C code, might give a hint.


#9

You write them by hand?
Strange that I didn’t think before that to suggest you to look at bindgen


#10

Wow, that’s an amazing thing! Thanks, got it figured out.