如何与GCC进行弱连接工作?

时间:2008-11-08 14:09:49

标签: c gcc linker weak

似乎有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();
}

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

Undefined symbols:
  "_asdf", referenced from:
      _asdf$non_lazy_ptr in ccFA05kN.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

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

5 个答案:

答案 0 :(得分:29)

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

与weak_import的弱链接实际上只适用于动态库。您可以使用静态链接(通过如上所述指定-undefined dynamic_lookup),但这不是一个好主意。这意味着在运行时之前不会检测到任何未定义的符号。我个人会在生产代码中避免这种情况。

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

这是f.c

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);
}

从f.c:

创建一个动态库
$ 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
what, no f?

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

答案 1 :(得分:6)

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

答案 2 :(得分:4)

您需要将MACOSX_DEPLOYMENT_TARGET变量设置为10.2或更高版本。有关弱链接,请参阅Apple's documentation及其technote

答案 3 :(得分:1)

最小的可运行Linux示例

main.c

#include <stdio.h>

int my_weak_var __attribute__((weak)) = 1;

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

notmain.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 upstream

因此我们看到,如果在notmain.o上给出,则非弱符号将按预期优先。

我们可以使用以下方法分析ELF object file符号:

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,具体说明如下:How to make gcc link strong symbol in static library to overwrite weak symbol?

弱符号也可以保留为未定义状态,这在Binutils中导致“特定于平台的行为”,请参见:GCC behavior for unresolved weak functions

在Ubuntu 18.10,GCC 8.2.0上进行了测试。

答案 4 :(得分:0)

来自gcc doc手册:

<强>弱

  

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

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