How to combine LTO with character versioning

I would like to compile a common library using both character versioning and connection time optimization (LTO). However, as soon as I turn on LTO, some exported characters disappear. Here is a minimal example:

We start by defining two implementations of the fun function:

$ cat fun.c #include <stdio.h> int fun1(void); int fun2(void); __asm__(".symver fun1, fun@v1 "); int fun1() { printf("fun1 called\n"); return 1; } __asm__(".symver fun2, fun@ @v2"); int fun2() { printf("fun2 called\n"); return 2; } 

Create a version of the script to ensure that only fun is exported:

 $ cat versionscript v1 { global: fun; local: *; }; v2 { global: fun; } v1; 

First attempt to compile without LTO:

 $ gcc -o fun.o -Wall -Wextra -O2 -fPIC -c fun.c $ gcc -o libfun.so.1 -shared -fPIC -Wl,--version-script,versionscript fun.o $ nm -D --with-symbol-versions libfun.so.1 | grep fun 00000000000006b0 T fun@ @v2 0000000000000690 T fun@v1 

.. exactly as it should be. But if I compile with LTO:

 $ gcc -o fun.o -Wall -Wextra -flto -O2 -fPIC -c fun.c $ gcc -o libfun.so.1 -flto -shared -fPIC -Wl,--version-script,versionscript fun.o $ nm -D --with-symbol-versions libfun.so.1 | grep fun 

.. no more characters exported.

What am I doing wrong?

+7
source share
3 answers

WHOPR Driver Design gives some powerful hints at what is happening. Function definitions fun1 and fun2 not exported according to the version scenario. The LTO plugin can use this information, and since GCC does not look into the asm directives, it knows nothing about the .symver directive and therefore removes the function definition.

Adding __attribute__ ((externally_visible)) is currently a workaround for this. You also need to create using -flto-partition=none so that .symver directives .symver not accidentally get into the intermediate assembler file, other than defining a function (where it will not have the desired effect).

GCC PR 48200 tracks the extension request for compiler-level version control of characters, which is likely to solve this problem as well.

+2
source

It looks like my externally_visible fix works. It:

 #define DLLEXPORT __attribute__((visibility("default"),externally_visible)) DLLEXPORT int fun1(void); 

Also see: https://gcc.gnu.org/onlinedocs/gccint/WHOPR.html

But I think your versioncript is wrong.

If I choose visibility overrides and change your versioncript by adding foo1 and foo2 , then it will work. How:

 v1 { global: fun; fun1; local: *; }; v2 { global: fun; fun2; } v1; 

Character aliases must be visible, as well as an alias.

+2
source

I just ran into the same problem - so thanks for asking this. However, I found that it is cleaner to use __attribute__((used)) . Since gcc does not scan top-level assembler, it cannot understand that fun1 and fun2 ... so it removes them. So it seems to me that changing the definition to:

 __asm__(".symver fun1, fun@v1 "); int __attribute__((used)) fun1() { printf("fun1 called\n"); return 1; } 

should be enough.

+1
source

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


All Articles