首页 > 解决方案 > 使用 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

标签: cgcccompiler-optimizationsimd

解决方案


推荐阅读