In C, how can I make attention alert for overflow of struct size during project increase
Example (my struct at the start of the project
//32 bytes size!
typedef struct
{
u8 Ver:8;
u8 HasAnyData:8;
u16 Ymin:16;
u16 Ymax:16;
u16 Xmax:16;
u16 PokerInterval:16;
u16 PokerDuration:16;
u16 MinimumWeight:16;
u32 tmp1:16;
u32 tmp2:32;
u32 tmp3:32;
u32 tmp4:32;
u32 tmp5:32;
} ConfData_t;
And after this definition, I read from flash memory
PP_ReadConfig(32,&confData);
Maybe problems if struct size will be less than 32 bytes
I should use the calculator for a mistake check.
Is there any standard way for a compiler config?
P.S Instead of sizeof(confData)
If you're using C11, consider _Static_assert to check sizeof at compile-time.
For example:
#include <assert.h>
typedef struct
{
u8 Ver:8;
u8 HasAnyData:8;
u16 Ymin:16;
u16 Ymax:16;
u16 Xmax:16;
u16 PokerInterval:16;
u16 PokerDuration:16;
u16 MinimumWeight:16;
u32 tmp1:16;
u32 tmp2:32;
u32 tmp3:32;
u32 tmp4:32;
u32 tmp5:32;
} ConfData_t;
int main(){
static_assert(sizeof(ConfData_t) < 32,
"needs to be < 32 bytes for flash");
}
This will fail to compile if you modify the struct to have a sizeof less than 32.
Related
I would like to return some strings to C via a Rust FFI call. I also would like to ensure they're cleaned up properly.
I'm creating the strings on the Rust side and turning them into an address of an array of strings.
use core::mem;
use std::ffi::CString;
#[no_mangle]
pub extern "C" fn drop_rust_memory(mem: *mut ::libc::c_void) {
unsafe {
let boxed = Box::from_raw(mem);
mem::drop(boxed);
}
}
#[no_mangle]
pub extern "C" fn string_xfer(strings_out: *mut *mut *mut ::libc::c_char) -> usize {
unsafe {
let s1 = CString::new("String 1").unwrap();
let s2 = CString::new("String 2").unwrap();
let s1 = s1.into_raw();
let s2 = s2.into_raw();
let strs = vec![s1, s2];
let len = strs.len();
let mut boxed_slice = strs.into_boxed_slice();
*strings_out = boxed_slice.as_mut_ptr() as *mut *mut ::libc::c_char;
mem::forget(boxed_slice);
len
}
}
On the C side, I call the Rust FFI function, print the strings and then attempt to delete them via another Rust FFI call.
extern size_t string_xfer(char ***out);
extern void drop_rust_memory(void* mem);
int main() {
char **receiver;
int len = string_xfer(&receiver);
for (int i = 0; i < len; i++) {
printf("<%s>\n", receiver[i]);
}
drop_rust_memory(receiver);
printf("# rust memory dropped\n");
for (int i = 0; i < len; i++) {
printf("<%s>\n", receiver[i]);
}
return 0;
}
This appears to work. For the second printing after the drop, I would expect to get a crash or some undefined behavior, but I get this
<String 1>
<String 2>
# rust memory dropped
<(null)>
<String 2>
which makes me less sure about the entire thing.
First you may want take a look at Catching panic! when Rust called from C FFI, without spawning threads. Because panic will invoke undefined behaviour in this case. So you better catch the panic or avoid have code that can panic.
Secondly, into_boxed_slice() is primary use when you don't need vector feature any more so "A contiguous growable array type". You could also use as_mut_ptr() and forget the vector. That a choice either you want to carry the capacity information into C so you can make the vector grow or you don't want. (I think vector is missing a into_raw() method but I'm sure you can code one (just an example) to avoid critical code repetition). You could also use Box::into_raw() followed with a cast to transform the slice to pointer:
use std::panic;
use std::ffi::CString;
pub unsafe extern "C" fn string_xfer(len: &mut libc::size_t) -> Option<*mut *mut libc::c_char> {
if let Ok(slice) = panic::catch_unwind(move || {
let s1 = CString::new("String 1").unwrap();
let s2 = CString::new("String 2").unwrap();
let strs = vec![s1.into_raw(), s2.into_raw()];
Box::into_raw(strs.into_boxed_slice())
}) {
*len = (*slice).len();
Some(slice as _)
} else {
None
}
}
Third, your drop_rust_memory() only drop a pointer, I think you are doing a total UB here. Rust memory allocation need the real size of the allocation (capacity). And you didn't give the size of your slice, you tell to Rust "free this pointer that contain a pointer to nothing (void so 0)" but that not the good capacity. You need to use from_raw_parts_mut(), your C code must give the size of the slice to the Rust code. Also, you need to properly free your CString you need to call from_raw() to do it (More information here):
use std::ffi::CString;
pub unsafe extern "C" fn drop_rust_memory(mem: *mut *mut libc::c_char, len: libc::size_t) {
let slice = Box::from_raw(std::slice::from_raw_parts_mut(mem, len));
for &x in slice.iter() {
CString::from_raw(x);
} // CString will free resource don't use mem/vec element after
}
To conclude, you should read more about undefined behaviour, it's not about "expect a crash" or "something" should happen. When your program trigger a UB, everything can happen, you go into a random zone, read more about UB on this amazing LLVM blog post
Note about C style prefer return the pointer and not the size because strings_out: *mut *mut *mut ::libc::c_char is a ugly thing so do pub extern fn string_xfer(size: &mut libc::size_t) -> *mut *mut libc::c_char. Also, How to check if function pointer passed from C is non-NULL
I have a struct returned to C code from Rust. I have no idea if it's a good way to do things, but it does work for rebuilding the struct and freeing memory without leaks.
#[repr(C)]
pub struct s {
// ...
}
#[repr(C)]
#[allow(clippy::box_vec)]
pub struct s_arr {
arr: *const s,
n: i8,
vec: Box<Vec<s>>,
}
/// Frees memory that was returned to C code
pub unsafe extern "C" fn free_s_arr(a: *mut s_arr) {
Box::from_raw(s_arr);
}
/// Generates an array for the C code
pub unsafe extern "C" fn gen_s_arr() -> *mut s_arr {
let many_s: Vec<s> = Vec::new();
// ... logic here
Box::into_raw(Box::new(s_arr {
arr: many_s.as_mut_ptr(),
n: many_s.len() as i8,
vec: many_s,
}))
}
The C header is currently written by hand, but I wanted to try out cbindgen. The manual C definition for s_arr is:
struct s_arr {
struct s *arr;
int8_t n;
void *_;
};
cbindgen generates the following for s_arr:
typedef struct Box_Vec_s Box_Vec_s;
typedef struct s_arr {
const s *arr;
int8_t n;
Box_Vec_s vec;
} s_arr;
This doesn't work since struct Box_Vec_s is not defined. Ideally I would just want to override the cbindgen type generated for vec to make it void * since it requires no code changes and thus no additional testing, but I am open to other suggestions.
I have looked through the cbindgen documentation, though not the examples, and couldn't find anything.
Your question is a bit unclear, but I think that if I understood you right, you're confusing two things and being led down a dark alley as a result.
In C, a dynamically-sized array, as you probably know, is identified by two things:
Its starting position, as a pointer
Its length
Rust follows the same convention - a Vec<_>, below the hood, shares the same structure (well, almost. It has a capacity as well, but that's beside the point).
Passing the boxed vector on top of a pointer is not only overkill, but extremely unwise. FFI bindings may be smart, but they're not smart enough to deal with a boxed complex type most of the time.
To solve this, we're going to simplify your bindings. I've added a single element in struct S to show you how it works. I've also cleaned up your FFI boundary:
#[repr(C)]
#[no_mangle]
pub struct S {
foo: u8
}
#[repr(C)]
pub struct s_arr {
arr: *mut S,
n: usize,
cap: usize
}
// Retrieve the vector back
pub unsafe extern "C" fn recombine_s_arr(ptr: *mut S, n: usize, cap: usize) -> Vec<S> {
Vec::from_raw_parts(ptr, n, cap)
}
#[no_mangle]
pub unsafe extern "C" fn gen_s_arr() -> s_arr {
let mut many_s: Vec<S> = Vec::new();
let output = s_arr {
arr: many_s.as_mut_ptr(),
n: many_s.len(),
cap: many_s.capacity()
};
std::mem::forget(many_s);
output
}
With this, cbindgen returns the expected header definitions:
typedef struct {
uint8_t foo;
} so58311426S;
typedef struct {
so58311426S *arr;
uintptr_t n;
uintptr_t cap;
} so58311426s_arr;
so58311426s_arr gen_s_arr(void);
This allows us to call gen_s_arr() from either C or Rust and retrieve a struct that is usable across both parts of the FFI boundary (so58311426s_arr). This struct contains all we need to be able to modify our array of S (well, so58311426S according to cbindgen).
When passing through FFI, you need to make sure of a few simple things:
You cannot pass raw boxes or non-primitive types; you will almost universally need to convert down to a set of pointers or change your definitions to accomodate (as I have done here)
You most definitely do not pass raw vectors. At most, you pass a slice, as that is a primitive type (see the point above).
You make sure to std::mem::forget() whatever you do not want to deallocate, and make sure to remember to deallocate it or reform it somewhere else.
I will edit this question in an hour; I have a plane to get on to. Let me know if any of this needs clarifications and I'll get to it once I'm in the right country :-)
I'm confused about this statement from the Rust Book:
There’s another advantage to using an enum rather than a struct: each variant can have different types and amounts of associated data. Version four type IP addresses will always have four numeric components that will have values between 0 and 255. If we wanted to store V4 addresses as four u8 values but still express V6 addresses as one String value, we wouldn’t be able to with a struct. Enums handle this case with ease:
#![allow(unused_variables)]
fn main() {
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
}
But when I tried it with structs to store V4 addresses as four u8 values but still express V6 addresses as one String value its also doing the same without any errors.
#[derive(Debug)]
struct IpAddr {
V4:(u8, u8, u8, u8),
V6:String,
}
fn main () {
let home = IpAddr {
V4: (127, 1, 1, 1),
V6: String::from("Hello"),
};
println!("{:#?}", home);
}
It's not the same. All enum elements have the very same size! The size of an enum element is the size of the largest variant plus the variant identifier.
With a struct it's a bit different. If we ignore padding, the size of the struct is the sum of the sizes of its members. With padding it will be a bit more:
fn main() {
let size = std::mem::size_of::<TheEnum>();
println!("Enum: {}", size * 8);
let size = std::mem::size_of::<TheStruct>();
println!("Struct: {}", size * 8);
}
struct TheStruct {
a: u64,
b: u8,
c: u64
}
enum TheEnum {
A(u64),
B(u8),
C(u64)
}
Here we can see the difference:
Enum: 128; 64 for the largest variant and 64 for the variant identifier.
Struct: 192; aligned to 64 bits, so we have 54 bits of padding
Another difference is in the way you use enums and structures. In an enum, you have to initialize only one of the variants. In your case - either IPv4 or IPv6. With a structure as in your example you have to provide both V4 and v6 address. You cannot provide only V4 or only V6.
I provide two functions for managing memory:
unsafe extern "system" fn alloc<A: Alloc>(
size: usize,
alignment: usize,
) -> *mut c_void { ... }
unsafe extern "system" fn free<A: Alloc>(
memory: *mut c_void
) { ... }
Both functions internally use the allocator-api.
These signatures cannot be changed. The problem is that free does not ask for size and alignment, which is required for Alloc::dealloc. To get around this, alloc allocates some extra space for one Layout. free can now access this Layout to get the needed extra data.
Recently, the allocator-api changed and instead of *mut u8 it now uses NonNull<Opaque>. This is where my problem occurs.
core::alloc::Opaque:
An opaque, unsized type. Used for pointers to allocated memory. [...] Such pointers are similar to C’s void* type.
Opaque is not Sized, so the use of NonNull::as_ptr().add() and NonNull::as_ptr().sub() are forbidden.
Previously, I used something like this (for simplicity, I assume Alloc's functions to be static):
#![feature(allocator_api)]
#![no_std]
extern crate libc;
use core::alloc::{Alloc, Layout};
use libc::c_void;
unsafe extern "system" fn alloc<A: Alloc>(
size: usize,
alignment: usize,
) -> *mut c_void
{
let requested_layout =
Layout::from_size_align(size, alignment).unwrap();
let (layout, padding) = Layout::new::<Layout>()
.extend_packed(requested_layout)
.unwrap();
let ptr = A::alloc(layout).unwrap();
(ptr as *mut Layout).write(layout);
ptr.add(padding)
}
The last line is not possible anymore with NonNull<Opaque>. How I can get around this?
I'd probably write it like this, using NonNull::as_ptr to get a *mut Opaque and then cast that to different concrete types:
#![feature(allocator_api)]
#![no_std]
extern crate libc;
use core::alloc::{Alloc, Layout};
use libc::c_void;
unsafe fn alloc<A: Alloc>(allocator: &mut A, size: usize, alignment: usize) -> *mut c_void {
let requested_layout = Layout::from_size_align(size, alignment).expect("Invalid layout");
let (layout, _padding) = Layout::new::<Layout>()
.extend_packed(requested_layout)
.expect("Unable to create layout");
let ptr = allocator.alloc(layout).expect("Unable to allocate");
// Get a pointer to our layout storage
let raw = ptr.as_ptr() as *mut Layout;
// Save it
raw.write(layout);
// Skip over it
raw.offset(1) as *mut _
}
unsafe extern "system" fn alloc<A: Alloc>(
This makes no sense to me. The various FFI ABIs ("C", "system", etc.) have no way of specifying Rust generic types. It seems deeply incorrect for this function to be marked extern.
Layout::new::<Layout>().extend_packed(requested_layout)
This seems likely to be very broken. As the documentation for Layout::extend_packed states, emphasis mine:
the alignment of next is irrelevant, and is not incorporated at all into the resulting layout.
Your returned pointer doesn't seem to honor the alignment request.
I'm trying to recreate a C struct with mixed bitfield members and "normal" members in Rust for FFI.
I've read that the bitflags crate would be the one to go with, unfortunately I find the documentation lacking on the regards how the syntax actually works.
The bitflags crate makes it easier to create bitmasks in a similar style as in C using enums. The bitfield crate claims to create bitfields that can be accessed, however I have no idea how it works.
I have a C structure like this:
struct mixed {
unsigned int flag_1_1 : 1;
unsigned int flag_2_7 : 7;
unsigned int flag_3_8 : 8;
unsigned int some_val1;
unsigned int some_val2;
unsigned int flag_4_16 : 16;
};
I have no clue on how to represent it in Rust, I'd use the crate libc to have access to c_uint, but other than that, I'm currently pretty much out of ideas and finding other code that does this has not proven successful:
#[repr(transparent)] // do I also need repr(C) ?
struct mixed {
flags_1_3: mixed_flags_1_3;
some_val1: c_uint;
some_val2: c_uint;
flags_4: mixed_flags_4;
}
bitfield!(
#[repr(transparent)] // do I also need repr(C), here too?
struct mixed_flags_1_3(u16 /* what about this parameter? */) {
u8; // what does this mean?
/* get/field, set: first bit, last bit; */
flag_1_1, _: 0, 0;
flag_2_7, _: 7, 1;
flag_3_8, _: 15, 8;
}
)
bitfield!(
#[repr(transparent)]
struct mixed_flags_4(u8) {
u8;
flag_4_16, _: 15, 0;
}
)
These are just guesses, how do I create a correct representation?
In cases like this you can look at genearted code by bindgen:
$ bindgen test.h
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage, Align>
where
Storage: AsRef<[u8]> + AsMut<[u8]>,
{
storage: Storage,
align: [Align; 0],
}
//skipped code with access method for bit fields
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct mixed {
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 2usize], u8>,
pub some_val1: ::std::os::raw::c_uint,
pub some_val2: ::std::os::raw::c_uint,
pub _bitfield_2: __BindgenBitfieldUnit<[u8; 2usize], u16>,
pub __bindgen_padding_0: u16,
}
Using rustc -- -Z unstable-options --pretty=expanded I think I could figure out that the macro does, and this seems to yield something that could be correct, however this is probably only compatible when the compiler does not try to pad or reorder the bitfields in the struct.
#[repr(transparent)] // do I also need repr(C) ?
struct mixed {
flags_1_3: mixed_flags_1_3;
some_val1: c_uint;
some_val2: c_uint;
flags_4: mixed_flags_4;
}
bitfield!(
#[repr(transparent)] // do I also need repr(C), here too?
// Results in a "tuple struct", ie. u16 = total size of bitfields
struct mixed_flags_1_3(u16) {
// All the following fields value should be treated as an
// unsigned int when accessed
c_uint;
/* get/field, set: first bit, last bit; */
flag_1_1, _: 0, 0;
flag_2_7, _: 7, 1;
// One could change the type again here, if one wanted to:
// u16
flag_3_8, _: 15, 8;
}
)
bitfield!(
#[repr(transparent)]
struct mixed_flags_4(u16) {
c_uint;
flag_4_16, _: 15, 0;
}
)
But for now at least I think I will just use libclang and bindgen as dependencies and generate my bindings automatically, due to the aforementioned problems with platform compat.