How to force gcc to call a function directly in the PIC code?

Consider the following function:

extern void test1(void); extern void test2(void) { test1(); } 

This gcc code generates without -fpic on amd64 Linux:

 test2: jmp test1 

When I compile with -fpic , gcc explicitly calls PLT to enable character interpolation:

 test2: jmp test1@PLT 

This, however, is not strictly necessary for position-independent code, and it can be ignored if I do not want to support it. If necessary, the linker in any case overwrites the purpose of the transition to the PLT symbol.

How can I, without changing the source code and making the compiled code unsuitable for the shared library, call function calls directly to their goals instead of explicitly going through the PLT?

+6
source share
2 answers

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.

+1
source

If you cannot change the source code, you can use the big hammer flag: -Bsymbolic linker:

When creating a shared library, link global symbol references to the definition in the shared library, if any. This is usually possible for a program associated with a shared library to override a definition in a shared library. This option only makes sense on ELF platforms that support shared libraries.

But be careful that it breaks if some parts of the library rely on character interpolation. I would recommend going with hidden functions that you don’t need to export (by setting latent visibility to them) or calling them using hidden aliases (specially designed for in-band calls in a PLT-less controlled way).

+2
source

Source: https://habr.com/ru/post/917178/


All Articles