Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting a void pointer (data) to a function pointer

I know this has been asked before but none of the cases I've seen here are like this one. I am importing some API functions at runtime, the general declaration on those functions would be like:

// Masks for UnmapViewOfFile and MapViewOfFile
typedef BOOL (WINAPI *MyUnmapViewOfFile)(LPCVOID);
typedef LPVOID (WINAPI *MyMapViewOfFile)(HANDLE, DWORD, DWORD, DWORD, SIZE_T);

// Declarations
MyUnmapViewOfFile LoadedUnmapViewOfFile;
MyMapViewOfFile LoadedMapViewOfFile;

I then call a generic "load" function where it calles GetProcAddress to get the address of the exported function from the proper DLL. That address is returned on a void**. This void** is one of the parameters in the generic load, something like:

int GenericLoad(char* lib, void** Address, char* TheFunctionToLoad)

and I would call this function:

void *Address;
GenericLoad("kernel32.dll", &Address, "UnmapViewOfFile");
LoadedUnmapViewOfFile = (MyUnmapViewOfFile) Address;

Or something similar to this. Now, of course the compiler complains about trying to cast a data void* to a function pointer. How do I do this then?

I've read countless sites and all kinds of nasty casts, so I'd appreciate it if you add code to the explanation.

Thanks Jess

like image 420
Jessica Avatar asked Dec 08 '09 15:12

Jessica


Video Answer


2 Answers

The correct code would be this one line:

GenericLoad("kernel32.dll", (void**)&LoadedUnmapViewOfFile, "UnmapViewOfFile");

What is done here basically is this: the address of the pointer variable (the one in which you want the address of the function to be put) is passed to GenericLoad - which is basically what it expects. void** was to denote "give me the address of your pointer". All the type casting is a magic around it. C would not allow specifying "a pointer to any function pointer", so the API author preferred void**.

like image 131
Pavel Radzivilovsky Avatar answered Sep 22 '22 04:09

Pavel Radzivilovsky


Now, of course the compiler complains about trying to cast a data void* to a function pointer

My compilers don't complain at all with your code (then again, I don't have warnings cranked up - I'm using more-or-less default options). This is with a variety of compilers from MS, GCC,and others. Can you give more details about the compiler and compiler options you're using and the exact warning you're seeing?

That said, C doesn't guarantee that a function pointer can be cast to/from a void pointer without problems, but in practice this will work fine on Windows.

If you want something that's standards compliant, you'll need to use a 'generic' function pointer instead of a void pointer - C guarantees that a function pointer can be converted to any other function pointer and back without loss, so this will work regardless of your platform. That's probably why the return value of the Win32 GetProcAddress() API returns a FARPROC, which is just a typedef for a function pointer to a function that takes no parameters (or at least unspecified parameters) and returns a pointer-sized int. Something like:

typedef INT_PTR (FAR WINAPI *FARPROC)();

FARPROC would be Win32's idea of a 'generic' function pointer. So all you should need to do is have a similar typedef (if you don't want to use FARPROC for some reason):

typedef intptr_t (*generic_funcptr_t)();    // intptr_t is typedef'ed appropriately elsewhere, 
                                            //    like in <stdint.h> or something

int GenericLoad(char* lib, generic_funcptr_t* Address, char* TheFunctionToLoad)

generic_funcptr_t Address;
GenericLoad("kernel32.dll", &Address, "UnmapViewOfFile");
LoadedUnmapViewOfFile = (MyUnmapViewOfFile) Address;

Or you can dispense with the middle-man and pass the pointer you really want to get the value into:

GenericLoad2("kernel32.dll", (generic_funcptr_t *) &LoadedUnmapViewOfFile, "UnmapViewOfFile");

Though that's more dangerous than the method using the intermediate variable - for example the compiler will not give a diagnostic if you leave off the ampersand in this last example, however in the previous example, it would generally give at least a warning if you left off the ampersand from the Address argument. Similar to Bug #1 here: http://blogs.msdn.com/sdl/archive/2009/07/28/atl-ms09-035-and-the-sdl.aspx

Now you should be set. However, any way you look at it you'll need to perform some dangerous casting. Even in C++ with templates you'd have to perform a cast at some level (though you might be able to hide it in the template function) because the GetProcAddress() API doesn't know the actual type of the function pointer you're retrieving.

Also note that your interface for GenericLoad() has a possibly serious design problem - it provides no way to manage the lifetime of the library. That may not be a problem if your intent is to not allow unloading a library, but it's something that users may want so you should consider the issue.

like image 20
Michael Burr Avatar answered Sep 19 '22 04:09

Michael Burr