I have been given a line of code that I know works, but I want to understand it better. Here's a snippet:
1 2 3 4 5 6
int main()
{
int addr; // assume addr gets a value (actually an address)
((void(*)(void))(*((int*)(addr+4))))();
return 0;
}
This code is used in a simple bootloader, and passes control of the program pointed to by addr.
I'd like to break this into bite-size pieces perhaps like this:
1 2 3
void (*pApp)();
pApp = (void *) addr + 4;
pApp();
(I can't test this currently, as I don't have access to the development hardware.) It compiles successfully, but...will it work? I'm particularly interested in whether the pointer arithmetic will be OK with the "+ 4".
This would only work on a platform where a value of type int is capable of holding a value of a pointer to data. I am going to replace your int with uintptr_t because otherwise it won't work on my system. If you don't have uintptr_t, size_t works on many platforms too.
#include <iostream>
#include <cstdint>
void f()
{
std::cout << "foo\n";
}
int main()
{
void(*fptr)() = f; // assuming C++, where f(void) and f() are the same
void(**fptrptr)() = &fptr;
std::uintptr_t addr = reinterpret_cast<std::uintptr_t>(fptrptr);
addr -= 4;
(
(void(*)(void)) // cast to function pointer the following value
(
*( // obtain the value pointed to by the following pointer
(std::uintptr_t*)( // cast to intptr_t pointer
addr+4 // add 4 to the memory address (a uintptr_t value)
)
)
)
)(); // invoke the function pointer
}
in short, your addr is the numeric value of a pointer to a pointer to a function, minus 4.
PS: Actually, this line would be more logical if you cast addr+4 to a pointer to pointer to function (since you're going to dereference it and invoke the result as a pointer to function anyway): (*(void(**)(void))(addr+4))(); or, in C++, (*reinterpret_cast<void(**)()>(addr+4))();
I should have mentioned that portability is really a non-issue for this passage of code. It's going onto an ARM device and some of the code I left out is hard-coded values to set up the stack and VTOR for the code that this branches to.
Regarding the "+ 4," I'm pretty sure this is to skip over the first four bytes of the image file (probably contains a checksum or something).
So, while your example is definitely worth studying for my education, I want to keep the code in this bootloader to something I can readily understand. Other than your proviso about int being the correct size for a pointer, can you see any showstoppers in my implementation?
No, the addr+4 should point to the start of a function. My interpretation of:
((void(*)(void))(*((int*)(addr+4))))();
Is that it's casting addr+4 to an int pointer, then casting that to a function pointer, and invoking it. No?
EDIT:
I may have gotten that wrong. Here's the entire code passage:
1 2 3 4
__set_MSP(*((int*)addr)); // setup stack pointer for the app
*((int*)0xE000ED08) = addr; // Setup the VTOR for the app
((void(*)(void))(*((int*)(addr+4))))(); // jump to the app
}
Edit: right, I should have said the value OF addr+4 is the address of an address of a function. The value AT addr+4 is indeed the address of a function.
Right...so how is that not a pointer to a function?
1. addr is cast to a pointer
2. addr is then dereferenced
3. what addr points to then becomes cast to a pointer to a void function
4. that pointer is then called
So, how is the value addr not simply an address to be branched to?