How go get address of slice as u64? - c

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?

Related

Parsing a binary packet (from C) in Go

Im writing a go application that listens for UDP packets over a network and parses them.
the udp packets are written in C and the struct definition (according to their docs) is something like the below. (Please understand that new to C and networking in general)
typedef struct foo
{
int code;
char seg[10];
char sym[25];
short type;
long amtToday;
long price;
...etc
} foo;
A sample network packet is something like the below
[233 3 0 0 99 100 101 95 102 111 0 0 0 0 55 52 51 57 0 69 69 68 49 48 50 48 74 65 78 50 48 50 49 0 0 58 254 127 0 0 1 0 166 58 254 127 0 0 255 255 255 255 255 255 255 255 32 232 141 0 0 0 0 0 0 135 166 58 254 127 0 0 ... etc]
in short, im having trouble getting the right values after the sym field.
i read up a bit about struct alignment in C and guessing that im ignoring the padded values. But im a bit confused as to where the padding occurs
is it this
typedef struct foo
{
int code;
char seg[10];
**char pad[6];**
char sym[25];
**char pad[7];**
short type;
long amtToday;
long price;
...etc
} foo;
i.e padding is added after each char field
or is it more like this
typedef struct foo
{
int code;
char seg[10];
char sym[25];
**char pad[1];**
short type;
long amtToday;
long price;
...etc
} foo;
the problem is that there is no way for me to determine if either of these are right at my end. I need to parse the entire struct before confirming - but unable to do so because of the padding issue
or am i heading in the wrong direction parsing this packet?
The best way to convince yourself of how it works is probably to write C-code that computes the offsets:
#include <stdio.h>
typedef struct foo
{
int code;
char seg[10];
char sym[25];
short type;
long amtToday;
long price;
} foo;
int main() {
// What are the memory offsets between individual struct members?
foo x;
printf(
"code: %ld, seg: %ld, sym: %ld, type: %ld, amtToday: %ld, price: %ld\n",
(long)&x.code - (long)&x,
(long)&x.seg - (long)&x,
(long)&x.sym - (long)&x,
(long)&x.type - (long)&x,
(long)&x.amtToday - (long)&x,
(long)&x.price - (long)&x
);
// How much space does the struct take up if we create an array for it?
foo y[2];
printf("offset: %ld\n", (long)&y[1] - (long)&y[0]);
return 0;
}
Output:
code: 0, seg: 4, sym: 14, type: 40, amtToday: 48, price: 56
offset: 64
The offsets can depend on architecture and the compiler being used. If you are able to edit the C-program, adding explicit padding to the struct might be the best way to guarantee the same offsets on 32-bit and 64-bit systems.

How properly place received data to structure (C)? [duplicate]

This question already has answers here:
How to use structure with dynamically changing size of data?
(3 answers)
Closed 4 years ago.
Question only for C. Vectors, lists and C++ do not solving.
I have buffer with received data:
(from there and further U8 is uin8_t (unsigned char) and so on)
buffer_pic
Data is packetized (it always has info about start, end and len).
Examples of data (hex):
(1 packet)
24 0C 00 02 00 00 00 11 AA 0D 78 C8
(2 packet)
24 0F 00 02 00 00 00 14 D0 07 00 00 0D 7D 53
Here:
'24' - start of packet
2 bytes of full packet len (bold)
4 bytes - special ID (here is 02 00 00 00)
1 byte commad
DATA block (makred as bold)
'0D' - end of packet
last 2 bytes - CRC
I want to use structures to work with this data.
Here is what I did:
typedef __packed struct FM_Packet_s
{
U8 head;
U16 len;
U32 uid;
U8 cmd;
U8 data;
U8 end;
U16 crc;
} FM_Packet_t, *FM_Packet_p;
U8 RX_buff[255];
…
FM_Packet_t *pFM_Packet = (FM_Packet_t *) &RX_buf;
handlerData()
{
// check received CRC
if(pFM_Packet->uid == ID_NUMBER)
{
if(pFM_Packet->cmd == NEEDED_COMMAND)
{
// command received, make actions
if (pFM_Packet->data == SPECIAL_DATA)
{
// do stuff
}
}
}
}
Everything was good until I received 2nd packet, which have more than 1 byte in DATA field. Now data is blended
Of course, field "data" may have different length, not only as showed in this two packets.
How can I handle (place into structures correctly) received data?
U8 data;
needs to be a pointer to a buffer that's the right size to hold your data, not an unsigned integer.
You would need to allocate the buffer to be whatever size you need, before loading the data, then point the pointer to it.
You could also just make your packet buffer a lot larger and use U16 len;to figure out where the data stops.
You can use the "unwarranted chumminess of C" http://computer-programming-forum.com/47-c-language/6c323b3186a9a335.htm
typedef __packed struct FM_Packet_s
{
U8 head;
U16 len;
U32 uid;
U8 cmd;
U8 data[1];
} FM_Packet_t, *FM_Packet_p;
It uses a flexible array - the [1] is just to keep the compiler quiet. On the nit-picking compilers, you may get warnings about array sizes. If we assign pFM_Packet->len to len,
The size of the array is len - 11
The end is at &pFM_Packet->data[len - 11]
The CRC is at &pFM_Packet->data[len - 10]
If you are using gcc, you can use the gcc extension which allows declaration of
U8 data[0];
This is illegal in most compilers but gcc allows it.

Extra Padding while sending a ethernet packet?

I was writing a code to send a packet to the other mac Address. I ma using a struct to create a packet as mentioned below:
typedef struct vlink_prm_in_s
{
vlink_header_t header;
uint32_t address;
uint16_t length;
}vlink_prm_in_t;
and when I want to send a packet I do following steps:
vlink_prm_in_t g_pkt;
g_pkt.header.verCmd = 0x43;
g_pkt.header.reverseVerCmd = ~(g_pkt.header.verCmd);
g_pkt.address = 0x11111111;
g_pkt.length = 0x2222;
memcpy(sendbuf+headerLen, &g_pkt, sizeof(g_pkt));
printf("%x\n", sendbuf[headerLen+4] );
payloadLen = sizeof(g_pkt);
sendbuf[headerLen+payloadLen] = 0xA5;
payloadLen++;
when I send the packet I get the following packet when I track it in WireShark:
aa bb cc dd ee 66 98 ee cb 03 be 1d ea e8 43 bc 00 00 11 11 11 11 22 22 00 00 a5
I don't know where do I get those extra zeros(highlighted) from ? Thanks.
After looking on internet more I found out that struct should be purely increasing or decreasing order (or all the variables should be same size). This way you can remove the extra zeros. So I did as follows:
typedef struct vlink_prm_in_s
{
vlink_header_t header;
uint16_t address1;
uint16_t address2;
uint16_t length;
}vlink_prm_in_t;
This fixed my problem.

Raw pointer turns null passing from Rust to 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>.

Create BMP header in C (can't limit 2 byte fields)

I'm doing it based on:
https://en.wikipedia.org/wiki/BMP_file_format
I want to create a BMP image from scratch in C.
#include <stdio.h>
#include <stdlib.h>
typedef struct HEADER {
short FileType;
int FileSize;
short R1;
short R2;
int dOffset;
} tp_header;
int main () {
FILE *image;
image = fopen("test.bmp", "w");
tp_header bHeader;
bHeader.FileType = 0x4D42;
bHeader.FileSize = 70;
bHeader.R1 = 0;
bHeader.R2 = 0;
bHeader.dOffset = 54;
fwrite(&bHeader, sizeof(struct HEADER), 1, image);
return 0;
}
I should be getting at output file:
42 4D 46 00 00 00 00 00 00 00 36 00 00 00
But instead i get:
42 4D 40 00 46 00 00 00 00 00 00 00 36 00 00 00
First of it should contain only 14 bytes. That "40 00" ruins it all. Is that the propper way of setting the header in C? How else can i limit the size in bytes outputed?
A struct might include padding bytes between the fields to align the next field to certain address offsets. The values of these padding bytes are indetermined. A typical layout might look like:
struct {
uint8_t field1;
uint8_t <padding>
uint8_t <padding>
uint8_t <padding>
uint32_t field2;
uint16_t field3;
uint8_t <padding>
uint8_t <padding>
};
<padding> is just added by the compile; it is not accessible by your program. This is just an example. Actual padding may differ and is defined by the ABI for your architecture (CPU/OS/toolchain).
Also, the order in which the bytes of a larger type are stored in memory (endianess) depends on the architecture. However, as the file requires a specific endianess, this might also have to be fixed.
Some - but not all - compilers alow to specify a struct to be packed (avoid padding), that still does not help with the endianess-problem.
Best is to serialize the struct properly by shifts and store to an uint8_t-array:
#include <stdint.h>
/** Write an uint16_t to a buffer.
*
* \returns The next position in the buffer for chaining.
*/
inline uint8_t *writeUInt16(uint8_t *bp, value)
{
*bp++ = (uint8_t)value;
*bp++ = (uint8_t)(value >> 8);
return bp;
}
// similar to writeUInt16(), but for uint32_t.
... writeUInt32( ... )
...
int main(void)
{
...
uint8_t buffer[BUFFER_SIZE], *bptr;
bptr = buffer;
bptr = writeUInt16(bptr, 0x4D42U); // FileType
bptr = writeUInt32(bptr, 70U); // FileSize
...
}
That will fill buffer with the header fields. BUFFER_SIZE has to be set according to the header you want to create. Once all fields are stored, write buffer to the file.
Declaring the functions inline hints a good compiler to create almost optimal code for constants.
Note also, that the sizes of short, etc. are not fixed. Use stdint.h types is you need types of defined size.
The problem is that your struct is aligned. You ought to write it like
#pragma pack(push, 1)
typedef struct HEADER {
short FileType;
int FileSize;
short R1;
short R2;
int dOffset;
} tp_header;
#pragma pack(pop)
Just for you to know — the compiler for optimizing reasons by default would lay it out like:
typedef struct HEADER {
short FileType;
char empty1; //inserted by compiler
char empty2; //inserted by compiler
int FileSize;
short R1;
short R2;
int dOffset;
} tp_header;
But you actually made also another error: sizeof(int) ≥ 4 bytes. I.e. depending on a platform integer could be 8 bytes. It is important, in such a cases you have to use types like int32_t from cstdint

Resources