I'm trying to load a function into a mapped memory buffer and call it later so I've made a test case to try out:
auto func() -> void{
asm(
"nop;"
"nop;"
"nop;"
"nop;"
);
}
auto main(int argc, char *argv[]) -> int{
void *exec_mem = mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
// check errors here
memcpy(exec_mem, reinterpret_cast<const void*>(func), 5); // size is known
(reinterpret_cast<void(*)()>(exec_mem))(); // function call
munmap(exec_mem, getpagesize());
}
Which works fine, but as soon as I try to do something even trivial I get a segfault.
I tried to do a simple variable assignment like this:
int x;
auto func() -> void{
x = 5;
}
and now my function call segfaults. I have changed the buffer size appropriately and am certain the correct memory is being written into the buffer.
What important piece of information am I missing here? Why can't I do this?
P.S. Please don't lecture me on unsafe code, this is a simple personal learning exercise.
Ignoring the fact that this is blatant undefined behavior, if you do an assignment of a global variable, the generated code is likely to use relative addressing to reference the variable on some architectures.
That is, the function expects itself and x to be at a given address, and if you move it, things break.
This is what my GCC generates for your test function:
Note the
movl $5, x(%rip)
, which means that the code uses its own address (stored in %rip) to compute the position ofx
and store 5 in it.So in short, there's no simple way to do what you're trying to do, unless you make sure that your function only has position-independent code. And even then, it's only asking for trouble.