VANILLA rust working with bytes

I’m going to be using Rust for quite a bit of very low-level stuff. One thing I would do in C is to directly map a section of contiguous bytes into a struct using a pointer.

For example, if I were parsing the ELF or PE header, I could just create a struct with all of the header members, load a ELF or PE file into a buffer, and then interpret the first 64 bytes of the buffer as a header struct by just moving the file pointer around by sizeof(IMAGE_DOS_HEADERS) or to move to the next header member, move the file ptr down by a WORD.

Another way would be to load it all into heap memory and index using sizeof() on that, instead of using the fp directly.

I needed to do something similar in Rust and I’m struggling to find an easy, standard way… Which was a surprise given that Rust is a systems level language. I’m aware of bincode, serde, etc… And I’ve used them in a pretty messy example… But I can’t help but wonder if there’s a simpler, more “built-in” way to just work with some raw bytes in Rust!

Thanks.

After making sure the buffer has read enough bytes, you could cast to *const IMAGE_DOS_HEADER and use ptr::read_unaligned to copy the value out.

It’s also possible to cast the pointer in-place, but you’d have to make sure the alignment is correct first.

2 Likes

The C way works the same in Rust, with same caveats about alignment, endianness, and padding.

You need to add #[repr(C)] to struct definition to stop Rust compiler from optimizing the struct layout.

However, I’d try something more rusty, like the nom crate. It has examples for several binary formats. The syntax is pretty nice for a parser, and repetitions and conditionals are much nicer than struct casting alternative. It is of course safer, can even handle incomplete buffers.

9 Likes

ah thanks for that pro tip on the struct optimization - I vaguely remember reading about it in the book but would have forgotten.

You may also want to look into the scroll crate. It’s essentially a type-safe version of this *const u8 to *const Foo cast. The benefits are that it’ll avoid issues around alignment and endianness (you usually specify endianness as a parameter), but I think it works by loading each field individually so may cost you one or two more cycles than an unsafe pointer cast.

I would also note that several crates exist for parsing ELF files already, i.e. the aptly named elf crate.

If you need #[no_std] support, keep in mind that not all crates offer support for it.

1 Like