在GCC中如何强制在运行时解析符号

6
我的第一篇帖子,充满了期望: 我正在尝试理解使用gcc进行静态链接、动态链接、共享库和静态库等相关内容。每次我尝试深入探讨此主题时,总会遇到一些我不太理解的东西。
一些实践工作:
bash$ cat main.c

#include "printhello.h"
#include "printbye.h"

void main()
{
PrintHello();
PrintBye();
}

bash$ cat printhello.h
void PrintHello();

bash$ cat printbye.h
void PrintBye();

bash$ cat printbye.c
#include <stdio.h>

void PrintBye()
{
printf("Bye bye\n");
}

bash$ cat printhello.c
#include <stdio.h>

void PrintHello()
{
printf("Hello World\n");
}

gcc -Wall -fPIC -c *.c -I.
gcc -shared -Wl,-soname,libcgreet.so.1 -o libcgreet.so.1.0   *.o
ln -sf libcgreet.so.1.0 libcgreet.so
ln -sf libcgreet.so.1.0 libcgreet.so.1

我已经创建了一个共享库。 现在我想将这个共享库与我的主程序链接起来,创建一个可执行文件。

gcc -Wall -L. main.c -lcgreet -o greet

它非常好用,如果我在运行greet之前设置LD_LIBRARY_PATH(或使用rpath选项链接),我就可以让它工作。

然而,我的问题是不同的: 既然我已经使用了共享库,那么难道不能强制在运行时解析符号(术语可能称为动态链接,如书籍“Linkers and Loaders”所述)吗?我明白我们可能不想这样做,因为这会使程序运行缓慢,并且每次运行程序都有开销,但我正在尝试理解这一点,以清楚我的概念。

gcc链接器是否提供任何选项来延迟运行时符号解析?(使用实际要运行程序的库进行操作)(如果库中有任何更改,则编译时可用的库可能与运行时可用的库不同) 我想能够像这样做:

bash$ gcc main.c -I.

(需要什么选项?) 所以我不必给出库名,只需告诉它我想在运行时进行符号解析,所以现在头文件已经足够了,实际库名不需要。

谢谢, 永远学习者。


http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html - adf88
请确保您使用适当的语言标记问题。它被标记为C++,但代码是C。请注意,为了能够使用纯动态库加载,C++中的代码将需要包含额外的“extern"C"”(在C中不需要也无效)。 - David Rodríguez - dribeas
2个回答

12
任何链接器(如gccld或其他)只能在编译时解析链接。这是因为ELF标准(像大多数其他标准一样)并没有像你所描述的那样定义“运行时”链接。它们要么静态链接(即lib.a),要么在启动时链接(lib.so,在加载ELF时必须存在)。但是,如果您使用动态链接,则链接器仅会在ELF中放置文件名和它必须找到的符号名称,它不直接链接该文件。因此,如果您以后想将库升级到新版本,只要系统可以找到相同的文件名(路径实际上可能不同)和相同的符号名称,就可以这样做。
另一种在运行时获取符号的选择是使用dlopen,它与gccld无关。简单地说,dlopen打开一个动态链接库,就像fopen可能会一样,并返回一个句柄,然后将该句柄传递给dlsym,并指定要查找的符号的名称,例如函数名称。然后dlsym将向您返回该符号的指针,您可以使用该指针调用该函数或将其用作变量。这就是插件的实现方式。

1
如果C ++语言标记正确,请记得将符号标记为 extern"C",以便它们在编译时不被混淆。 - David Rodríguez - dribeas

2
我认为您正在寻找ld选项“--unresolved-symbols=ignore-all”,是的,它确实可以做到这一点(忽略先前的答案)。想象一下这样的情况:共享库在程序已经运行时才被加载,它可以使用主进程已经解析/加载的所有符号,无需再次进行。顺便说一句,在Linux上,它并不会使其变慢。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接