I'm trying to create an FFI for a C function that takes a pointer to a struct and modifies it. I've tested the C code and gotten the values that I am expecting, but when I try and print the values from Rust I get completely different results. The element in question is a slice of u32.
The code looks something like this:
C:
struct my_struct {
uint64_t len;
uint32_t state[4];
int count;
};
int modify_struct(struct my_struct *x)
{
// modify here
// prints the expected values
uint8_t *bytes = (uint8_t *)x->state;
for (int i = 0; i < 16; i++)
printf("%02x", bytes[i]);
}
Rust:
#[repr(C)]
#[derive(Debug, Default)]
struct MyStruct {
length: u64,
state: [u32; 4],
count: libc::c_int
}
extern "C" {
fn modify_struct(x: *mut MyStruct) -> libc::c_int;
}
fn main() {
let mut x = MyStruct::default();
unsafe { modify_struct(&mut x); }
for i in 0..4 {
println!("{:08x}", x.state[i]); // the values here differ from what the C code prints
}
}
I print the value in C right before returning to Rust, and then print in Rust right after and the values are completely different.
I found the issue. It wasn't a problem in the FFI or C code, I was getting tripped up on endianess. I've edited the code above so it's more clear where the issue is (and better reflects the actual code).
I realized that when I print in Rust, I'm concatenating the actual 32-bit numbers together:
for i in 0..4 {
println!("{:08x}", x.state[i]); // the values here differ from what the C code prints
}
But what I was unknowingly looking for was the exact (little endian) order in memory, which the C code was printing like so:
uint8_t *bytes = (uint8_t *)x->state;
for (int i = 0; i < 16; i++)
printf("%02x", bytes[i]);
The solution for me was to change the way Rust represents the struct from:
struct MyStruct {
length: u64,
state: [u32; 4],
count: libc::c_int
}
to:
struct MyStruct {
length: u64,
state: [u8; 16],
count: libc::c_int
}
Which allows me to access the data in the exact byte order I'm looking for.