Using mmap with /dev/mem - Is it okay to use reinterpret_cast

Viewed 147

I'm using mmap with /dev/mem. I've seen examples in C use the following pattern:

#define OFFSET = ...;
int fd = 0;
void* base;

fd = open("/dev/mem", ...);

base = mmap(..., fd, ...);

// Below is line of interest.
*((uint32_t*)(base + OFFSET)) = 23;

First question - What is happening here?

It looks like we are adding an offset value to a void*, then casting it to uint32_t* and then assigning a number to it. Why can't we just declare base as uint32_t*? Why cast it just before assigning it?

Second question - How would I do this in C++?

The following works from bits and pieces on the net. But it's basically me trying whack-a-mole with reinterpret_cast and static_cast and seeing which one gives me the right result and doesn't throw errors or warnings. Also replaced void* with uint8_t* to prevent compiler warning me of arithmetic on void pointer. I don't know why it works or if it's even the right way to do it. Help me not shoot myself in the foot?

#define OFFSET = ...;
int fd = 0;
uint8_t* base;

fd = open("/dev/mem", ...);

base = reinterpret_cast<uint8_t*>(mmap(..., fd, ...));

*(reinterpret_cast<uint32_t*>(base + OFFSET)) = 23;
1 Answers

base + OFFSET is a gcc extension where pointer arithmetic works on void *. "Why can't we just declare base as uint32_t*?" Because the arithmetic would come out wrong. void * arithmetic is in bytes.

There seems to be some confusion as to the meaning of arithmetic in bytes. OFFSET is the number of bytes (not 32 bit integers) from base where the value 23 needs to go; so casting to uint32_t first would move four times as far. This kind of code is typical of code that writes to heterogeneous data-structures directly rather than using a struct. There's pros and cons of declaring a struct vs. writing the accesses out directly. In the ancient days they usually declared a struct, but the pendulum has swung back because of alignment issues. It's easier to ensure the compiler doesn't hose the struct definition by inserting padding by writing it out longhand.

base = reinterpret_cast<uint8_t*>(mmap(..., fd, ...));
*(reinterpret_cast<uint32_t*>(base + OFFSET)) = 23;

This is indeed what you'd have to do to say it in standard C++; though for code like this people tend to use C style casts in C++ (not going to debate it; just pointing it out).

Related