If you declare test1()
hidden ( __attribute__((__visibility__("hidden")))
, the jump will be direct.
Now test1()
cannot be defined as hidden in its original translation system, but I believe that there should be no harm from this discrepancy, except for the C language guarantee that &test1 == &test1
could be violated for you at runtime if one of the pointers was obtained through a hidden link and one through a public one (a public link could be inserted via preload or DSO, which was before the current one in the search area, while a hidden link (which leads to direct jumps) effectively prevented any kind of interposition)
A better way to deal with this would be to define two names for test1()
- the public name and the private / hidden name.
In gcc and clang, this can be done with some alias magic, which can only be done in the translation unit that defines the character.
Macros can make it more beautiful:
#define PRIVATE __attribute__((__visibility__("hidden"))) #define PUBLIC __attribute__((__visibility__("default"))) #define PRIVATE_ALIAS(Alias,OfWhat) \ extern __typeof(OfWhat) Alias __attribute((__alias__(#OfWhat), \ __visibility__("hidden"))) #if HERE PUBLIC void test1(void) { } PRIVATE_ALIAS(test1__,test1); #else PUBLIC void test1(void); PRIVATE void test1__(void); #endif void call_test1(void) { test1(); } void call_test1__(void) { test1__(); } void call_ext0(void) { void ext0(void); ext0(); } void call_ext1(void) { PRIVATE void ext1(void); ext1(); }
The above compilation (-O3, x86-64) in:
call_test1: jmp test1@PLT call_test1__: jmp test1__ call_ext0: jmp ext0@PLT call_ext1: jmp ext1
(The definition HERE = 1 additionally builds a call to test1, since it is small, and local and -O3 are turned on).
Example in https://godbolt.org/g/eZvmp7 mode.
-fno-semantic-interposition
will also do the job, but it also violates the C language guarantee, and itβs kind of a big hammer that does not have alias details.