7

我想使用符号版本控制和链接时优化 (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

..不再导出符号。

我究竟做错了什么?

4

3 回答 3

4

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

目前,添加__attribute__ ((externally_visible))是解决此问题的方法。您还需要使用 进行构建-flto-partition=none,这样.symver指令就不会意外地落在与函数定义不同的中间汇编程序文件中(在那里它不会产生预期的效果)。

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

于 2019-01-04T20:48:48.020 回答
3

看起来我的externally_visible修复工作。这是:

#define DLLEXPORT __attribute__((visibility("default"),externally_visible))

DLLEXPORT int fun1(void);

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

但我认为你的版本是错误的。

如果我去掉可见性覆盖并通过添加更改您的版本脚本fun1fun2然后它就可以工作了。喜欢:

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

符号别名目标必须和别名一样可见。

于 2017-09-20T21:09:52.847 回答
1

我刚刚遇到了同样的问题 - 所以感谢您提出这个问题。但是我发现它使用起来更干净__attribute__((used))。由于 gcc 没有扫描顶级汇编程序,它无法弄清楚fun1并且fun2正在使用......所以它删除了它们。所以在我看来,将定义更改为:

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

应该足够了。

于 2019-09-10T14:58:23.560 回答