c - 使用 gcc target_clones 函数多版本控制静态链接存档时如何修复未定义的引用链接器错误
问题描述
我将 target_clones GCC 属性用于几个函数的运行时优化 SIMD 版本,其中一些声明为静态的,而另一些则由同一静态库中的其他对象使用。后者在头文件中具有带有 target_clones 属性的声明。所有对象都构建良好,并使用 ar 组装成一个静态存档。最终的应用程序链接阶段在包含静态存档时失败,对库公共函数的版本化符号有未定义的引用错误。
更新:添加了 gcc 错误报告,以防这是:https ://gcc.gnu.org/bugzilla/show_bug.cgi?id=91664
我在这里创建了一个测试应用程序来说明这个问题https://github.com/elementgreen/fmv-test 它有 3 个 make 目标。第一个“make works”只是从 .c 文件一步编译测试应用程序。第二个“make Also_works”将每个 .c 文件编译成目标 .o 文件,然后用 gcc -o 链接这些文件。第三个目标“make borken”不起作用并说明了问题。每个 .c 文件被编译成 .o 目标文件,然后用 ar 创建一个静态存档,然后使用 gcc 将静态存档链接到最终应用程序中。
下面是我放在github上的测试应用的内容:
主.c:
#include <stdlib.h>
#include <string.h>
#include "fmv-test.h"
#define ARRAY_SIZE 100000
int
main (int argc, char **argv)
{
double *dArray;
dArray = malloc (ARRAY_SIZE * sizeof (double));
memset (dArray, 0, ARRAY_SIZE * sizeof (double));
fmv_test (dArray, ARRAY_SIZE);
return 0;
}
fmv-test.h:
#ifndef __FMV_TEST_H__
#define __FMV_TEST_H__
#define SIMD_CLONE __attribute__ ((__target_clones__ ("avx2","avx","sse4.1","sse2","default")))
double fmv_test (double *dArray, int size) SIMD_CLONE;
#endif
fmv-test.c:
#include "fmv-test.h"
static void internal_func (double *dArray, int size) SIMD_CLONE;
double
fmv_test (double *dArray, int size)
{
double result;
int i;
internal_func (dArray, size);
for (i = 0; i < size; i++)
result += dArray[i];
return result;
}
static void
internal_func (double *dArray, int size)
{
int i;
for (i = 0; i < size; i++)
dArray[i] += 1.0;
}
生成文件:
works:
@echo "This works"
gcc -o fmv-test fmv-test.c main.c
also_works:
@echo "This also works"
gcc -c fmv-test.c
gcc -c main.c
gcc -o fmv-test fmv-test.o main.o
borken:
@echo "This doesn't work"
gcc -c fmv-test.c
gcc -c main.c
ar cr fmv-test.a fmv-test.o main.o
gcc -o fmv-test-borken fmv-test.a
将静态存档与 gcc 函数多版本化链接应该可以工作。相反,它失败并出现以下错误:
/usr/bin/ld: fmv-test.a(main.o): in function `fmv_test':
main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x1f): undefined reference to `fmv_test.avx2.0'
/usr/bin/ld: main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x3b): undefined reference to `fmv_test.avx.1'
/usr/bin/ld: main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x57): undefined reference to `fmv_test.sse4_1.2'
/usr/bin/ld: main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x71): undefined reference to `fmv_test.sse2.3'
/usr/bin/ld: main.c:(.text.fmv_test.resolver[fmv_test.resolver]+0x7a): undefined reference to `fmv_test.default.4'
collect2: error: ld returned 1 exit status
似乎目标文件在各种符号上以不同的编号结尾结束,但这些仍然设法与“also_works”目标链接,所以我认为这不是问题(参见下面的 objdump 输出)。这是gcc中的错误吗?我在 Ubuntu 19.04 上使用 8.3.0 版本。
objdump -t fmv-test.o | grep fmv_test
0000000000000000 l F .text 0000000000000062 fmv_test.default.9
0000000000000239 l F .text 0000000000000062 fmv_test.avx2.4
000000000000029b l F .text 0000000000000062 fmv_test.avx.5
00000000000002fd l F .text 0000000000000062 fmv_test.sse4_1.6
000000000000035f l F .text 0000000000000062 fmv_test.sse2.7
0000000000000000 l d .text.fmv_test.resolver 0000000000000000 .text.fmv_test.resolver
00000000000003c1 g i .text 0000000000000080 internal_func._GLOBAL___fmv_test.ifunc
0000000000000000 w F .text.fmv_test.resolver 0000000000000080 fmv_test.resolver
0000000000000000 g i .text.fmv_test.resolver 0000000000000080 fmv_test
objdump -t main.o | grep fmv_test
0000000000000000 l d .text.fmv_test.resolver 0000000000000000 .text.fmv_test.resolver
0000000000000000 g i .text.fmv_test.resolver 0000000000000080 fmv_test
0000000000000000 w F .text.fmv_test.resolver 0000000000000080 fmv_test.resolver
0000000000000000 *UND* 0000000000000000 fmv_test.avx2.0
0000000000000000 *UND* 0000000000000000 fmv_test.avx.1
0000000000000000 *UND* 0000000000000000 fmv_test.sse4_1.2
0000000000000000 *UND* 0000000000000000 fmv_test.sse2.3
0000000000000000 *UND* 0000000000000000 fmv_test.default.4