picking apart a function pointer

mzimmers (516)
Hi -

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".

Thanks for any clarification.
Cubbi (1572)
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#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
}

online demo: http://ideone.com/PwbgfX

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))();
Last edited on
mzimmers (516)
Hey, Cubbi -

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?

Thanks.
Cubbi (1572)
As long as the value at addr+4 is indeed the address of an address of a function, there's no problem with this.

To make it really easy to understand, I would have written maybe
1
2
3
   typedef void(*fptr_t)(void);
   fptr_t* fptrptr = (fptr_t*)(addr+4);
   (*fptrptr)();
mzimmers (516)
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
 }


So maybe it is a pointer to a pointer of sorts.
Last edited on
Cubbi (1572)
no, it's cast, dereference, cast, function call.

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.


   vtor   fptr
|_|_|_|_|_|_|_|_|
 ↑       ↑
addr    addr+4
Last edited on
mzimmers (516)
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?
Cubbi (1572)
the value *(addr+4) is the address to be branched to. The value addr+4 is the address of that address.
mzimmers (516)
OK, then, I must not understand how the VTOR works. (No big surprise there.) I'll review it tomorrow and make my change accordingly.

Thanks for the help.
Registered users can post here. Sign in or register to post.