Function interposition: about the use of a static function variable

78 Views Asked by At

I want to interpose a library function from a third party, using LD_PRELOAD, and I have seen the following code pattern in a lot of places:

void interposed_func()
{
    static void(*real_func)() = NULL;

    if (real_func == NULL)
       real_func = (void(*)())dlsym(RTLD_NEXT, "interposed_func");

    real_func();
}

It calls to my attention that if at each call of the function. What's wrong with just:

void interposed_func()
{
    static void(*real_func)() = (void(*)())dlsym(RTLD_NEXT, "interposed_func");
    real_func();
}

Since real_func is a static function local variable it will only be initialized once right? I don't understand why all of the examples I have faced uses an additional but (in my view) unnecesary if. Is there any else to consider that I'm not aware of?

2

There are 2 best solutions below

3
dbush On

Objects with static storage duration (i.e. those declared at file scope or with the static keyword) may only be initialized with a constant expression, as such initialization effectively happens at compile time.

Your second example does not qualify because a function call is not a constant expression. It would be the same as trying to initialize a file scope variable with a function call.

The first example is required to satisfy the requirement of a constant initializer. The overhead of a NULL check on each access is minimal.

Also, the conversion on this line:

real_func = (void(*)())dlsym(RTLD_NEXT, "interposed_func");

Is technically not allowed by the C standard, as it converts an object pointer to a function pointer. The proper conversion would be this:

*(void **)(&real_func) = dlsym(RTLD_NEXT, "interposed_func");

As noted in the man page for dlsym in an example:

   /* Writing: cosine = (double (*)(double)) dlsym(handle, "cos");
       would seem more natural, but the C99 standard leaves
       casting from "void *" to a function pointer undefined.
       The assignment used below is the POSIX.1-2003 (Technical
       Corrigendum 1) workaround; see the Rationale for the
       POSIX specification of dlsym(). */

   *(void **) (&cosine) = dlsym(handle, "cos");
1
0___________ On

None is correct.

  1. The second code will not compile in C.

  2. The code doesn't check if the symbol exists. If dlsym does not exist you will be calling a NULL pointer converted to a function pointer which is UB

void interposed_func()
{
    static void(*real_func)() = NULL;

    if (real_func == NULL)
       real_func = dlsym(0, "interposed_func");

    if(real_func) real_func();
}