Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine LTO with symbol versioning

Tags:

c

gcc

linker

lto

I would like to compile a shared library using both symbol versioning and link-time optimization (LTO). However, as soon as I turn on LTO, some of the exported symbols vanish. Here is a minimal example:

Start by defining two implementations of a function fun:

$ 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 script to ensure that only fun is exported:

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

First attempt, 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 symbols exported anymore.

What am I doing wrong?

like image 203
Nikratio Avatar asked Sep 19 '17 15:09

Nikratio


2 Answers

WHOPR Driver Design gives some strong hints to what is going on. The function definitions fun1 and fun2 are not exported according to the version script. The LTO plugin is able to use this information, and since GCC does not peek into the asm directives, it knows nothing about the .symver directive, and therefore removes the function definition.

For now, adding __attribute__ ((externally_visible)) is the workaround for this. You also need to build with -flto-partition=none, so that the .symver directives do not land by accident in a different intermediate assembler file than the function definition (where it will not have the desired effect).

GCC PR 48200 tracks an enhancement request for symbol versioning at the compiler level, which would likely address this issue as well.

like image 124
Florian Weimer Avatar answered Oct 02 '22 13:10

Florian Weimer


It looks like my externally_visible fix works. This is:

#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 versionscript is wrong.

If I take out the visibility overrides and change your versionscript by adding fun1 and fun2 then it works. Like:

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

The symbol alias targets have to be visible as well as the alias.

like image 31
Zan Lynx Avatar answered Oct 02 '22 13:10

Zan Lynx