1

Let's say that there exists a kernel header like this in C:

typedef struct {
  uint32_t type;
  uint32_t length;
  uint32_t extra;
  uint32_t flags;

  uint32_t reserved0;
  uint32_t reserved1;

  uint32_t magic;
  uint32_t crc32;
} zbi_header_t;

That is, the fist bytes of the kernel image are this struct.

In order to parse this header into Rust, I'd have something like this:

#[derive(Debug)]
#[repr(C)]
pub struct ZbiHeader {
    zbi_type: u32,
    length: u32,
    extra: u32,
    flags: u32,
    reserved0: u32,
    reserved1: u32,
    magic: u32,
    crc32: u32,
}

notice that I added a repr(C).

What is the best Rust way to transform a slice &[u8] of size sizeof(zbi_header_t) into this struct?

Can I take a byte array and deserialize it into a struct? talks about alignment issues but would we have it here?

In this particular case where all values are uint32_t I think the compiler wouldn't do any paddings, but I don't know if this is guaranteed and also I'm thinking about what if there was an u8 in the middle. It could be that the compiler always disables the paddings for this file? By the way, the header is from https://fuchsia.googlesource.com/fuchsia/+/6d0df1b8f7cbadf04553501f995156b224dd6347/zircon/system/public/zircon/boot/image.h#102 but I couldn't find any rules saying it should ignore paddings, etc.

Rafaelo
  • 33
  • 1
  • 15

2 Answers2

1

There are multiple ways to go about doing this. The easiest way is if the byte slice is known to be well aligned.

let header:&ZbiHeader = unsafe{ &*slice.as_ptr().cast() };

Things get a little more complicated if the byte slice is unaligned. In that case, you have to use ptr::read_unaligned instead.

let header:ZbiHeader = unsafe{ slice.as_ptr().cast().read_unaligned() };

I believe the struct you have shown does not have any padding, and has the same memory representation as an [u32;8]. Look here for more about the C's struct layout. If your struct did have padding it wouldn't be initialized, so you shouldn't use an &[u8] you should use a &[MaybeUninit<u8>] instead. Or if possible, pass around the struct itself or a pointer to it instead of a byte array.

Aiden4
  • 2,504
  • 1
  • 7
  • 24
-1

Padding won't be an issue - since you have #[repr(C)], the representation of the Rust ZbiHeader will match the representation of the C zbi_header_t, padding and all.

As for the alignment issues, it entirely depends on how you get your &[u8] byte slice. If you know for a fact that it's a zbi_header_t casted to a byte pointer, then the pointer should be well aligned. If you're just grabbing a random &[u8] slice, ex from a file input buffer, then you'd have to worry.

Colonel Thirty Two
  • 23,953
  • 8
  • 45
  • 85
  • how should I deserialize the bytes into the struct in Rust then? – Rafaelo Jul 30 '21 at 09:04
  • "Padding won't be an issue" - it will be, if there's any, since padding bytes are uninitialized, and `&[u8]` cannot have uninitialized bytes. – Cerberus Jul 30 '21 at 20:02