Can't use IFUNC resolver with lazy binding

Viewed 202

System specs:

$ uname -a
Linux 5.4.0-66-generic #74-Ubuntu SMP Wed Jan 27
22:54:38 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ gcc -v
...
gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) 
$ inxi
CPU: Quad Core Intel Core i7-1065G7 (-MT MCP-) speed/min/max: 1002/400/3900 MHz 
Kernel: 5.4.0-66-generic x86_64 Up: 6d 7h 59m Mem: 4831.2/7730.1 MiB (62.5%) 
Storage: 476.94 GiB (28.7% used) Procs: 349 Shell: bash 5.0.17 inxi: 3.0.38 

I am trying to use IFUNC with lazy binding, but no matter what I do, the resolver keeps running before main is called - Which means that the resolver runs before runtime.

There is no BIND_NOW symbol in the target ELF.

LD_BIND_NOW isn't set (I tried to do the same while binding it to 0).

I can't see any reason in the documentation about why this is happening.

main.c:

#include <stdio.h>
#include <stdlib.h>


int foo_1() { return 1; }
int foo_2() { return 2; }
int foo_3() { return 3; }

extern int foo(void);
int foo(void) __attribute__((ifunc ("resolve_foo")));


int main() {
    printf("main started\n");
    printf("foo() = %d\n", foo());
    return 0;
}


static void *resolve_foo(void) {
    int res = 0;//atoi(getenv("FOO")); 
    printf("resolver started\n");
    if (res == 1)
        return foo_1;
    if (res == 2)
        return foo_2;
    return foo_3;
    
}

Executing:

$ gcc -zlazy -o t main.c
$ ./t
resolver started
main started
foo() = 3
1 Answers

Lazy binding allows to bind at the latest possible time. However, the resolver by ifunc attribute is not really related to linking/binding, but more or less a convenience for the user of the function (it has some implications beyond just that).

As the documentation explains, the function tagged that way is implicitly changed to an indirect function call. The (hidden) function pointer to the actual implementation is provided by the resolver. And the resolver is called before main is called, i.e. strictly speaking before the C application code starts. This is necessary to guarantee the compliant behaviour when main is called under all circumstances.

What you expect is the resolver is called at the latest possible time, but that would either require the run-time code to be changed, depending on flow to catch all possible situations (including taking the pointer to the run-time resolved function or conditions), or calling the resolver each and every time before the actual function is called.

To understand this in detail, please also keep in mind that the binary is not C language anymore. The wrapped function could also be called from inline assember the first time, so there would be hardly a way around the second option, because the compiler-generated code cannot be aware of these. Similar applies for other linked libraries.

I think it should be clear calling the resolver each time would add quite some overhead and clearly contradict the main intention of the whole concept.

The ifunc mechanism exists exactly to avoid this situation. It also allows for optimiations, e.g. the compiler directly inserting indirect calls without making the function pointer global.

So, if you want to resolve after main, you need to provide your own mechanism using a dedicated function pointer. In that case you are responsible, of course to resolve the pointer yourself and either call the function via pointer (global pointer) or use a wrapper function with a static pointer (yet not local to the function, of course).


Said that, I wonder what your actual problem is here. I suspect you want your resolver to rely on something in main. That is a bad idea anyway - at least for standard functions (memcpy()/memclr() e.g. might even be called by the startup code). For user-functions, I gave you an alternative approach above.


Update: I just found this bugreport about ifunc which sheds more light on it and also states the problem calling other functions. That because before main() the C environment might not be completely operational. This is expecially true for stdio. So your printf() call could also be a problem by itself.

Related