Raw pointer turns null passing from Rust to C - c

I'm attempting to retrieve a raw pointer from on C function in rust, and use that same raw pointer as an argument in another C function from another library. When I pass the raw pointer, I end up with a NULL pointer on the C side.
I have tried to make a simplified version of my issue, but when I do it works as I would expect it to -
C Code -
struct MyStruct {
int value;
};
struct MyStruct * get_struct() {
struct MyStruct * priv_struct = (struct MyStruct*) malloc( sizeof(struct MyStruct));
priv_struct->value = 0;
return priv_struct;
}
void put_struct(struct MyStruct *priv_struct) {
printf("Value - %d\n", priv_struct->value);
}
Rust Code -
#[repr(C)]
struct MyStruct {
value: c_int,
}
extern {
fn get_struct() -> *mut MyStruct;
}
extern {
fn put_struct(priv_struct: *mut MyStruct) -> ();
}
fn rust_get_struct() -> *mut MyStruct {
let ret = unsafe { get_struct() };
ret
}
fn rust_put_struct(priv_struct: *mut MyStruct) {
unsafe { put_struct(priv_struct) };
}
fn main() {
let main_struct = rust_get_struct();
rust_put_struct(main_struct);
}
When I run this I get the output of Value - 0
~/Dev/rust_test$ sudo ./target/debug/rust_test
Value - 0
~/Dev/rust_test$
However, when trying to do this against a DPDK library, I retrieve and pass a raw pointer in the the same way but get a segfault. If I use gdb to debug, I can see that I'm passing a pointer on the Rust side, but I see it NULL on the C side -
(gdb) frame 0
#0 rte_eth_rx_queue_setup (port_id=0 '\000', rx_queue_id=<optimized out>, nb_rx_desc=<optimized out>, socket_id=0, rx_conf=0x0, mp=0x0)
at /home/kenton/Dev/dpdk-16.07/lib/librte_ether/rte_ethdev.c:1216
1216 if (mp->private_data_size < sizeof(struct rte_pktmbuf_pool_private)) {
(gdb) frame 1
#1 0x000055555568953b in dpdk::ethdev::dpdk_rte_eth_rx_queue_setup (port_id=0 '\000', rx_queue_id=0, nb_tx_desc=128, socket_id=0, rx_conf=None,
mb=0x7fff3fe47640) at /home/kenton/Dev/dpdk_ffi/src/ethdev/mod.rs:32
32 let retc: c_int = unsafe {ffi::rte_eth_rx_queue_setup(port_id as uint8_t,
In frame 1, mb has an address and is being passed. In frame 0 the receiving function in the library is showing it as 0x0 for mp.
My code to receive the pointer -
let mb = dpdk_rte_pktmbuf_pool_create(CString::new("MBUF_POOL").unwrap().as_ptr(),
(8191 * nb_ports) as u32 , 250, 0, 2176, dpdk_rte_socket_id());
This calls into an ffi library -
pub fn dpdk_rte_pktmbuf_pool_create(name: *const c_char,
n: u32,
cache_size: u32,
priv_size: u16,
data_room_size: u16,
socket_id: i32) -> *mut rte_mempool::ffi::RteMempool {
let ret: *mut rte_mempool::ffi::RteMempool = unsafe {
ffi::shim_rte_pktmbuf_pool_create(name,
n as c_uint,
cache_size as c_uint,
priv_size as uint16_t,
data_room_size as uint16_t,
socket_id as c_int)
};
ret
}
ffi -
extern {
pub fn shim_rte_pktmbuf_pool_create(name: *const c_char,
n: c_uint,
cache_size: c_uint,
priv_size: uint16_t,
data_room_size: uint16_t,
socket_id: c_int) -> *mut rte_mempool::ffi::RteMempool;
}
C function -
struct rte_mempool *
rte_pktmbuf_pool_create(const char *name, unsigned n,
unsigned cache_size, uint16_t priv_size, uint16_t data_room_size,
int socket_id);
When I pass the pointer, it looks much the same as my simplified version up above. My variable mb contains a raw pointer that I pass to another function -
ret = dpdk_rte_eth_rx_queue_setup(port,q,128,0,None,mb);
ffi library -
pub fn dpdk_rte_eth_rx_queue_setup(port_id: u8,
rx_queue_id: u16,
nb_tx_desc: u16,
socket_id: u32,
rx_conf: Option<*const ffi::RteEthRxConf>,
mb_pool: *mut rte_mempool::ffi::RteMempool ) -> i32 {
let retc: c_int = unsafe {ffi::rte_eth_rx_queue_setup(port_id as uint8_t,
rx_queue_id as uint16_t,
nb_tx_desc as uint16_t,
socket_id as c_uint,
rx_conf,
mb)};
let ret: i32 = retc as i32;
ret
}
ffi -
extern {
pub fn rte_eth_rx_queue_setup(port_id: uint8_t,
rx_queue_id: uint16_t,
nb_tx_desc: uint16_t,
socket_id: c_uint,
rx_conf: Option<*const RteEthRxConf>,
mb: *mut rte_mempool::ffi::RteMempool ) -> c_int;
}
C function -
int
rte_eth_rx_queue_setup(uint8_t port_id, uint16_t rx_queue_id,
uint16_t nb_rx_desc, unsigned int socket_id,
const struct rte_eth_rxconf *rx_conf,
struct rte_mempool *mp);
I apologize for the length, but I feel like I'm missing something simple and haven't been able to figure it out. I've checked struct alignment for each field that is being passed, and I even see values for the pointer that is received as I'd expect -
(gdb) frame 1
#1 0x000055555568dcf4 in dpdk::ethdev::dpdk_rte_eth_rx_queue_setup (port_id=0 '\000', rx_queue_id=0, nb_tx_desc=128, socket_id=0, rx_conf=None,
mb=0x7fff3fe47640) at /home/kenton/Dev/dpdk_ffi/src/ethdev/mod.rs:32
32 let retc: c_int = unsafe {ffi::rte_eth_rx_queue_setup(port_id as uint8_t,
(gdb) print *mb
$1 = RteMempool = {name = "MBUF_POOL", '\000' <repeats 22 times>, pool_union = PoolUnionStruct = {data = 140734245862912}, pool_config = 0x0,
mz = 0x7ffff7fa4c68, flags = 16, socket_id = 0, size = 8191, cache_size = 250, elt_size = 2304, header_size = 64, trailer_size = 0,
private_data_size = 64, ops_index = 0, local_cache = 0x7fff3fe47700, populated_size = 8191, elt_list = RteMempoolObjhdrList = {
stqh_first = 0x7fff3ebc7f68, stqh_last = 0x7fff3fe46ce8}, nb_mem_chunks = 1, mem_list = RteMempoolMemhdrList = {stqh_first = 0x7fff3ebb7d80,
stqh_last = 0x7fff3ebb7d80}, __align = 0x7fff3fe47700}
Any ideas on why the pointer is turning to NULL on the C side?

CString::new("…").unwrap().as_ptr() does not work. The CString is temporary, hence the as_ptr() call returns the inner pointer of that temporary, which will likely be dangling when you use it. This is “safe” per Rust's definition of safety as long as you don't use the pointer, but you eventually do so in a unsafe block. You should bind the string to a variable and use as_ptr on that variable.
This is such a common problem, there is even a proposal to fix the CStr{,ing} API to avoid it.
Additionally raw pointer are nullable by themselves, so the Rust FFI equivalent of const struct rte_eth_rxconf * would be *const ffi::RteEthRxConf, not Option<*const ffi::RteEthRxConf>.

Related

How go get address of slice as u64?

I transfered this C struct:
85 struct spi_ioc_transfer {
86 __u64 tx_buf;
87 __u64 rx_buf;
88
89 __u32 len;
90 __u32 speed_hz;
91
92 __u16 delay_usecs;
93 __u8 bits_per_word;
94 __u8 cs_change;
95 __u32 pad;
96
97 /* If the contents of 'struct spi_ioc_transfer' ever change
98 * incompatibly, then the ioctl number (currently 0) must change;
99 * ioctls with constant size fields get a bit more in the way of
100 * error checking than ones (like this) where that field varies.
101 *
102 * NOTE: struct layout is the same in 64bit and 32bit userspace.
103 */
104 };
to Rust:
struct SpiIocTransfer {
tx_buf: u64,
rx_buf: u64,
len: u32,
speed_hz: u32,
delay_usecs: u16,
bits_per_word: u8,
cs_change: u8,
pad: u32
/* If the contents of 'struct spi_ioc_transfer' ever change
* incompatibly, then the ioctl number (currently 0) must change;
* ioctls with constant size fields get a bit more in the way of
* error checking than ones (like this) where that field varies.
*
* NOTE: struct layout is the same in 64bit and 32bit userspace.
*/
}
However, I'm having problems in filling tx_buf and rx_buf:
C version:
int wiringPiSPIDataRW (int channel, unsigned char *data, int len)
{
struct spi_ioc_transfer spi ;
channel &= 1 ;
memset (&spi, 0, sizeof (spi)) ;
spi.tx_buf = (unsigned long)data ;
spi.rx_buf = (unsigned long)data ;
spi.len = len ;
My Rust version:
pub fn spi_data_rw(&mut self, channel_: i32, data: &[u8], len: u32) -> i32 {
let channel = channel_ & 1;
let spi = SpiIocTransfer::new();
spi.tx_buf = data.as_mut_ptr();//(unsigned long) ?
spi.rx_buf = data.as_mut_ptr();//(unsigned long) ?
Error:
error[E0308]: mismatched types
--> src/w25q.rs:68:29
|
68 | spi.rx_buf = data.as_mut_ptr();//(unsigned long) ?
| ^^^^^^^^^^^^^^^^^ expected `u64`, found *-ptr
|
= note: expected type `u64`
found raw pointer `*mut u8`
How do I get the address of the data slice as an u64?

How can Rust transfer struct back to C?

I am building a server client app, where server is Rust and obtaining stat information for a given path and transfer it to C client. I want the C client to be able to directly use the bytes, and cast it to a struct. The stat I am talking about is this struct in C. Here is my Rust representation of the stat:
#[repr(C)]
pub struct Stat {
st_dev: u64,
st_ino: u64,
st_nlink: u64,
st_mode: u32,
st_uid: u32,
st_gid: u32,
st_rdev: u64,
st_size: u64,
st_blksize: u64,
st_blocks: u64,
st_atime: i64,
st_atime_nsec: i64,
st_mtime: i64,
st_mtime_nsec: i64,
st_ctime: i64,
st_ctime_nsec: i64
}
impl Stat {
pub fn encode(self) -> Vec<u8> {
unsafe {
std::slice::from_raw_parts(
(&self as *const Stat) as *const u8,
std::mem::size_of::<Stat>()
).to_owned()
}
}
}
However, when I find the values are not matching once I received it from my C side. Below is the value comparison for each field following the order in the struct,
# C:
16777220
8613988721
0
0
5
0
16832
6879832142633762816
0
1327895242430480384
20
687194767360
17592186044416
0
6879832142633762816
0
#Rust:
16777220
8613988721
5
16832
501
20
0
160
4096
0
1601835746
0
1601835746
0
1601835746
309174704
Does anyone know what caused this problem? And how can I solve it?
Use nix. See https://docs.rs/nix/newest/nix/sys/stat/fn.stat.html
nix uses the struct stat from the libc package, which has a separate manually generated struct definition for every supported platform. I don't precisely understand why you want to encode stat structures, but you need to keep in mind that they will most likely be mutually incompatible betweeen different architechtures, platforms, and OS versions. That is, you can only reliably bytewise encode and decode them when the encoder and decoder are running on the same version of the same platform.

Accessing memory loaded via tftp

I am trying to access data from a file that I load in via tftp. I'm using an AM3358 processor
tftp 81000000 mydata
and I can see the data being correctly loaded
=> md 81000000
81000000: 00004000 00000000 00002000 00000400 .#....... ......
In the u-boot code, I create a pointer to this address and then attempt to de-reference it, but the value is incorrect which makes me think I'm using the incorrect address
unsigned long addr = 0x81000000;
uint32_t *ptr = &addr;
uint32_t val = *(ptr+0);
printf("addr %ul val: %ul", addr, val);
Furthermore, I'm trying to load the address of mydata into a 32-bit LCD register, but the physical address of 0x81000000 is beyond that of a 32-bit number. I believe I'm just confused as to what address mapping is involved here.
bdi yields
=> bdi
arch_number = 0x00000000
boot_params = 0x80000100
DRAM bank = 0x00000000
-> start = 0x80000000
-> size = 0x20000000
baudrate = 115200 bps
TLB addr = 0x9fff0000
relocaddr = 0x9ffb4000
reloc off = 0x1f7b4000
irq_sp = 0x9df8ba90
sp start = 0x9df8ba80
Early malloc usage: 4a8 / 1000
fdt_blob = 0x9df8bea0
Why would 0x81000000 not be a valid 32 bit number ?
0x00000000 <= 0x81000000 <= 0xFFFFFFFF.
I think there may be an error in your logic: you are initializing ptrwith the address of addr, not the address of its content.
The correct code would rather be something like:
uint32_t addr = 0x81000000;
uint32_t *ptr = (uint32_t*)(uintptr_t) addr;
uint32_t val = *(ptr+0);
printf("addr %ul val: %ul", addr, val);
This can be tested on your PC - you may need to add support for building 32 bit applications, i.e. execute sudo apt-get install gcc-multilib on Ubuntu.
ptr.c:
#include <stdio.h>
#include <stdint.h>
int
main ()
{
uint32_t addr = 0x81000000; // gcc 9.3.0 is complaining about uint32_t *ptr = &addr;
// your code
{
uint32_t *ptr = &addr;
printf ("%p\n", ptr);
}
// correct code
{
uint32_t *ptr = (uint32_t *) (uintptr_t) addr;
printf ("%p\n", ptr);
}
}
gcc -m32 -o ptr ptr.c
./ptr
0xff83bc60
0x81000000
This would be why you cannot access the content of the file you transferred using TFTP, you are reading from an incorrect, but valid address.

Is C.GoBytes required to retrieve a C buffer, or is a pointer sufficient here?

The cgo code below has a function to put a Go value in a C buffer, and two alternative functions to get it back; getViaGoBytes and getDirect.
Is getViaGoBytes any better than getDirect?
I assume not, and the intermediary slice created in getViaGoBytes is unnecessary.
Am I correct in thinking Go allocates enough memory when the uint64 y variable is declared, and the assignment to y copies the memory from C to Go?
package main
/*
char buf[8];
void put(char * input, int size) {
while (size--) {
buf[size] = input[size];
}
}
*/
import "C"
import "unsafe"
func put(input uint64) {
C.put((*C.char)(unsafe.Pointer(&input)), C.int(unsafe.Sizeof(input)))
}
func getViaGoBytes() uint64 {
var out uint64
data := C.GoBytes(unsafe.Pointer(&(C.buf[0])), C.int(unsafe.Sizeof(out)))
out = *(*uint64)(unsafe.Pointer(&data[0]))
return out
}
func getDirect() uint64 {
return *(*uint64)(unsafe.Pointer(&(C.buf[0])))
}
func main() {
var input uint64 = 1<<64 - 1
println(input)
put(input)
var x uint64 = getViaGoBytes()
println(x)
var y uint64 = getDirect()
println(y)
}
Marking question answered by copying JimB's answer from comment:
GoBytes copies a C allocated buffer into a slice with Go allocated
memory. If that's what you want, then use GoBytes. Here you're not
even keeping that copy, so there's no reason to do it.
Also, benchmark is interesting:
$ go test -bench . -benchmem
BenchmarkGoBytes-8 20000000 97.8 ns/op 32 B/op 3 allocs/op
BenchmarkDirect-8 2000000000 0.84 ns/op 0 B/op 0 allocs/op
PASS

Adding a value to an integer stored as a byte array

What would be the best way to add (not append) a value to an array of bytes where the array is treated as a single integer?
For example:
let arr = [0xFF, 0x01, 0xC3, 0x43];
Assume arr may be of any length. If I add 350 to this, for example, the new array should be: [0xFF, 0x01, 0xC4, 0xA1]. The solution I've come up with only works if we're incrementing by 1, therefore I'd need to call the method in a loop amount times, which can be inefficient with large amount's (this example uses Vec's instead of an array):
fn increment_byte_vec(vec: Vec<u8>) -> Vec<u8> {
let mut done = false;
vec.iter().rev().map(|&v| {
if done {
v
} else if v == 0xFF {
0
} else {
done = true;
v + 1
}
}).rev().collect::<Vec<_>>()
}
How would I adapt the above so that the function can take an amount parameter?
Not much to say here; just add and carry along the vector, back to front.
It's possible for the number to overflow. I chose to return the carry; you might prefer to extend the vector. My solution uses mutation, since it's much more efficient than allocating a new vector, and since I wasn't changing the length I thought it was nicer to go generic over a mutable slice.
/// Increments the bytes, assuming the most significant
/// bit is first, and returns the carry.
fn increment_bytes(b256: &mut [u8], mut amount: u64) -> u64 {
let mut i = b256.len() - 1;
while amount > 0 {
amount += b256[i] as u64;
b256[i] = amount as u8;
amount /= 256;
if i == 0 { break; }
i -= 1;
}
amount
}
fn main() {
let mut input = vec![0xFF, 0x01, 0xC3, 0x43];
println!("{}", increment_bytes(&mut input, 350));
println!("{:?}", input);
}
You are basically implementing sum in base 256. One way of tackling this is to convert to decimal, add 350 and reconvert the result to base 256.
I.e. [0xFF, 0x01, 0xC3, 0x43] is:
255 (FF) * 256^3 +
1 * 256^2 +
195 (C3) * 256^1 +
67 (43) * 256^0 = 4,278,305,603 (base10)
4,278,305,603 + 350 = 4_278_305_953
Now you need to reconvert this to base 256. This last bit might look like this in Rust:
// warning, does not handle overflows
fn base10_to_256(n: u64) -> [u8; 4] {
let mut converted_number = [0u8; 4];
let mut number_to_convert = n;
let base = 256u64;
for index in 0.. {
converted_number[3-index] = (number_to_convert % base) as u8;
number_to_convert = number_to_convert / base;
if number_to_convert == 0 { break; }
}
converted_number
}
playground

Resources