Create a function with a unique function pointer at runtime

When calling WinAPI functions that take callbacks as arguments, it is common to use a special parameter to pass some arbitrary data to the callback. If there is no such thing (for example SetWinEventHook

), the only way to understand which of the API calls resulted in a given callback being called is to have different callbacks. When we know all the cases in which a given API is called at compile time, we can always create a class template with a static method and create it using different template arguments on different sides of the call. It's a hell of a job and I don't like it.

How do I create callback functions at runtime so that they have different function pointers?

I saw a solution (sorry, in Russian) with assembly generation at runtime, but it was not portable across the x86 / x64 architecture.

+3


source to share


2 answers


You can use the closure API libffi . This allows you to create trampolines, each with a different address. I've implemented the wrapping class here , although it's not finished yet (only supports arguments int

and return type, you can specialize detail::type

to support more than just int

). The heavier alternative is LLVM, although if you're only dealing with C types libffi will do the job just fine.



+2


source


I came up with this solution, which should be portable (but I haven't tested it):

#define ID_PATTERN          0x11223344
#define SIZE_OF_BLUEPRINT   128   // needs to be adopted if uniqueCallbackBlueprint is complex...

typedef int (__cdecl * UNIQUE_CALLBACK)(int arg);


/* blueprint for unique callback function */
int uniqueCallbackBlueprint(int arg)
{    
    int id = ID_PATTERN;

    printf("%x: Hello unique callback (arg=%d)...\n", id, arg);

    return (id);
}


/* create a new unique callback */
UNIQUE_CALLBACK createUniqueCallback(int id)
{
    UNIQUE_CALLBACK result = NULL;
    char *pUniqueCallback;
    char *pFunction;
    int pattern = ID_PATTERN;
    char *pPattern;
    char *startOfId;
    int i;
    int patterns = 0;


    pUniqueCallback = malloc(SIZE_OF_BLUEPRINT);

    if (pUniqueCallback != NULL)
    {
        pFunction = (char *)uniqueCallbackBlueprint;
#if defined(_DEBUG)
        pFunction += 0x256;  // variable offset depending on debug information????
#endif /* _DEBUG */
        memcpy(pUniqueCallback, pFunction, SIZE_OF_BLUEPRINT);
        result = (UNIQUE_CALLBACK)pUniqueCallback;


        /* replace ID_PATTERN with requested id */
        pPattern = (char *)&pattern;
        startOfId = NULL;
        for (i = 0; i < SIZE_OF_BLUEPRINT; i++)
        {
            if (pUniqueCallback[i] == *pPattern)
            {
                if (pPattern == (char *)&pattern)
                    startOfId = &(pUniqueCallback[i]);
                if (pPattern == ((char *)&pattern) + sizeof(int) - 1)
                {
                    pPattern = (char *)&id;
                    for (i = 0; i < sizeof(int); i++)
                    {
                        *startOfId++ = *pPattern++;
                    }
                    patterns++;
                    break;
                }

                pPattern++;
            }
            else
            {
                pPattern = (char *)&pattern;
                startOfId = NULL;
            }
        }

        printf("%d pattern(s) replaced\n", patterns);

        if (patterns == 0)
        {
            free(pUniqueCallback);
            result = NULL;
        }
    }

    return (result);
}

      

Usage looks like this:

int main(void)
{
    UNIQUE_CALLBACK callback;
    int id;
    int i;

    id = uniqueCallbackBlueprint(5);
    printf("  -> id = %x\n", id);

    callback = createUniqueCallback(0x4711);
    if (callback != NULL)
    {
        id = callback(25);
        printf("  -> id = %x\n", id);
    }

    id = uniqueCallbackBlueprint(15);
    printf("  -> id = %x\n", id);

    getch();
    return (0);
}

      



I noticed interesting behavior when compiling with debug information (Visual Studio). The address obtained with pFunction = (char *)uniqueCallbackBlueprint;

is disabled by a variable number of bytes. The difference can be obtained with a debugger that displays the correct address. This offset varies from assembly to assembly, and I'm guessing it has something to do with debug information? This is not a problem for the release build. So maybe it should be put into a library that is being built as a "release".

Another thing to consider is that there should be byte alignment pUniqueCallback

, which could be a problem. But aligning the beginning of a function to 64-bit boundaries isn't hard to add to this code.

Internally, pUniqueCallback

you can implement whatever you want (note to update SIZE_OF_BLUEPRINT so you don't miss the tail of your function). The function is compiled and the generated code is reused at runtime. The original id value is replaced when the unique function is created, so the drawing function can handle it.

+2


source







All Articles