28

似乎有 3 种方法可以告诉 GCC 对符号进行弱链接:

  • __attribute__((weak_import))
  • __attribute__((weak))
  • #pragma weak symbol_name

这些都不适合我:

#pragma weak asdf
extern void asdf(void) __attribute__((weak_import, weak));
...
{
    if(asdf != NULL) asdf();
}

我总是收到这样的链接错误:

未定义的符号:
  “_asdf”,引用自:
      _asdf$non_lazy_ptr 在 ccFA05kN.o
ld:未找到符号
collect2: ld 返回 1 个退出状态

我在 OS X 10.5.5 上使用 GCC 4.0.1。我究竟做错了什么?

4

5 回答 5

34

我只是调查了一下,并认为其他一些人可能对我的发现感兴趣。

与weak_import 的弱链接实际上只适用于动态库。您可以让它与静态链接一起工作(通过指定 -undefined dynamic_lookup 如上所述)但这不是一个热门的想法。这意味着在运行之前不会检测到未定义的符号。这是我个人在生产代码中会避免的事情。

这是一个 Mac OS X 终端会话,展示了如何使其工作:

这里是 fc

int f(int n)
{
    return n * 7;
}

这是whatnof.c

#include <stdio.h>
#include <stdlib.h>

extern int f (int) __attribute__((weak_import));

int main() {
    if(f == NULL)
        printf("what, no f?\n");
    else
        printf("f(8) is %d\n", f(8));
    exit(0);
}

从 fc 创建一个动态库:

$ cc -dynamiclib -o f.dylib f.c

针对动态库编译和链接,列出动态库。

$ cc -o whatnof whatnof.c f.dylib
$ otool -L whatnof
whatnof:
       f.dylib (compatibility version 0.0.0, current version 0.0.0)
       /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.0)

运行 whatnof 看看会发生什么:

$ whatnof
f(8) is 56

现在将 f.dylib 替换为空库(无符号):

$ mv f.dylib f.dylib.real
$ touch null.c
$ cc -dynamiclib -o f.dylib null.c

运行同样的 whatnof 看看会发生什么:

$ whatnof
what, no f?

weak_import 的基本思想(或“用例”)是它允许您链接一组动态(共享)库,然后针对相同库的早期版本运行相同的代码。您可以对照 NULL 检查函数,以查看它们是否在当前运行代码的特定动态库中受支持。这似乎是 Xcode 支持的基本开发模型的一部分。我希望这个例子有用;它有助于让我对 Xcode 设计的这一部分放心。

于 2010-09-20T08:34:27.077 回答
7

添加-Wl,-flat_namespace,-undefined,dynamic_lookup到用于执行最终链接的 gcc 编译器行。

于 2009-04-08T14:27:19.123 回答
5

最小的可运行 Linux 示例

主程序

#include <stdio.h>

int my_weak_var __attribute__((weak)) = 1;

int main(void) {
    printf("%d\n", my_weak_var);
}

不是main.c

int my_weak_var = 2;

编译并运行这两个对象:

gcc -c -std=c99 -Wall -Wextra -pedantic -o main.o main.c
gcc -c -std=c99 -Wall -Wextra -pedantic -o notmain.o notmain.c
gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.o notmain.o
./main.out

输出:

2

编译和运行没有notmain.o

gcc -std=c99 -Wall -Wextra -pedantic -o main.out main.o
./main.out

输出:

1

GitHub 上游.

所以我们看到,如果给定 on notmain.o,那么非弱符号按预期优先。

我们可以通过以下方式分析ELF 目标文件符号:

nm main.o notmain.o

这使:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
0000000000000000 V my_weak_var
                 U printf

notmain.o:
0000000000000000 D my_weak_var

接着:

man nm

包含:

符号类型。至少使用以下类型;其他的也取决于目标文件格式。如果是小写,符号通常是本地的;如果是大写,则符号是全局的(外部的)。然而,对于特殊的全局符号(“u”、“v”和“w”)显示了一些小写符号。

"D"
"d" 符号在初始化数据段中。

"V"
"v" 符号是弱对象。当弱定义符号与正常定义符号链接时,使用正常定义符号不会出错。当链接一个弱未定义符号且该符号未定义时,该弱符号的值变为零且没有错误。在某些系统上,大写表示已指定默认值。

但是,如果处理.a静态库,您可能必须-Wl,--whole-archive按照以下说明使用:如何使 gcc 在静态库中链接强符号以覆盖弱符号?

弱符号也可以未定义,这在 Binutils 中会导致“平台特定行为”,请参阅:GCC behavior for unresolved weak functions

在 Ubuntu 18.10、GCC 8.2.0 上测试。

于 2019-02-08T23:09:31.017 回答
4

您需要将 MACOSX_DEPLOYMENT_TARGET 变量设置为 10.2 或更高版本。请参阅Apple 的文档及其关于弱链接的技术说明。

于 2008-11-08T15:32:51.150 回答
1

从 gcc 文档手册:

虚弱的

weak 属性导致声明作为弱符号而不是全局符号发出。这主要用于定义可以在用户代码中覆盖的库函数,尽管它也可以与非函数声明一起使用。ELF 目标支持弱符号,使用 GNU 汇编器和链接器时也支持 a.out 目标。

这意味着一个对象被合法地覆盖一个弱符号(在另一个对象/库中定义)而不会在链接时出错。不清楚的是您是否将库与符号链接。似乎您都没有定义符号并且库没有正确链接。

于 2008-11-08T15:05:39.473 回答