如何将LTO与符号版本控制相结合

时间:2017-09-19 15:47:10

标签: c gcc linker lto

我想使用符号版本控制和链接时优化(LTO)编译共享库。但是,只要我打开LTO,一些导出的符号就会消失。这是一个最小的例子:

首先定义函数 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;
}

创建版本脚本以确保仅导出 fun

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

首次尝试,在没有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

..完全应该如此。但是如果我用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

..不再导出符号。

我做错了什么?

3 个答案:

答案 0 :(得分:2)

看起来我的return this.http .get<any>(`api/heroes/?name=${term}`) .map(res => res.data); 修复工作正常。这是:

externally_visible

另见:https://gcc.gnu.org/onlinedocs/gccint/WHOPR.html

但我认为你的appscript是错误的。

如果我通过添加#define DLLEXPORT __attribute__((visibility("default"),externally_visible)) DLLEXPORT int fun1(void); foo1取出可见性覆盖并更改您的Versionscript,那么它就可以了。像:

foo2

符号别名目标必须是可见的以及别名。

答案 1 :(得分:2)

WHOPR Driver Design为正在发生的事情提供了一些有力的提示。函数定义fun1fun2不会根据版本脚本导出。 LTO插件能够使用此信息,并且由于GCC不会窥视asm指令,因此它对.symver指令一无所知,因此删除了函数定义。

目前,添加__attribute__ ((externally_visible))是解决此问题的方法。您还需要使用-flto-partition=none进行构建,以使.symver指令不会偶然降落在与函数定义不同的中间汇编文件中(在该汇编文件中不会产生预期的效果)。

GCC PR 48200在编译器级别跟踪对符号版本控制的增强请求,这也可能会解决此问题。

答案 2 :(得分:1)

我遇到了同样的问题-感谢您提出这个问题。但是,我发现使用__attribute__((used))更干净。由于gcc并未扫描顶级汇编程序,因此无法确定正在使用fun1fun2…因此将其删除。因此,在我看来,将定义更改为:

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

应该足够。