5

我使用 Windows 主机上的 RVDS 编译器使用目标代码文件(C 源代码)创建了一个共享库 ( *.so) 。*.o

我将此共享对象与一个应用程序(gcc用于 Linux 主机上的 ARM 目标)链接并获得一个可执行文件,该可执行文件在运行时会产生分段错误。(我知道我必须调试它!)

如果我创建一个具有相同源文件的静态库,而不是创建共享库,然后与应用程序链接,然后执行应用程序,它可以按预期正常工作。

所以我的问题是:

  1. 我是否需要使用某些结构在我的源文件中显式导出符号(导出到应用程序的函数)或任何其他符号,以便在与应用程序链接时正常工作?需要什么,我该怎么做?

  2. 共享库是如何工作的?即,函数将被加载和运行的地址,将在创建库时在库中给出。应用程序(main())如何解析要执行库函数的地址?

  3. 静态库是如何工作的,即在静态库的情况下,这个地址规范和解析是如何发生的?

4

3 回答 3

13

这是它在 linux 上的工作方式:

1)不,你不需要做任何事情。但是,您可以使用 gcc-fvisibility命令行参数限制导出变量,并使用可见性属性显式标记导出的条目。

2) 可执行文件将具有它导入的所有函数的表(这些都是具有默认可见性的函数)。加载器/链接器将在运行之前选择一个地址来加载库并填充此表,对这些函数的调用是间接调用。(请注意,这也适用于共享对象)

3) 静态链接是在链接时执行的(在你编译之后)。实际地址在程序集中被替换,它们是直接调用。

注意:有一种叫做 PIC(位置无关代码)的东西。AFAIK,这处理对同一共享对象中的数据/函数的引用,因此链接器在加载库时不需要覆盖库的一半代码,因为代码不会对其进行任何绝对引用自己的数据。你可以尝试一下。

于 2009-04-16T14:32:49.460 回答
3
  1. 您不需要使用 导出符号gcc,因为它默认导出所有符号;但是,RVDS 可能会也可能不会这样做。检查您的 RVDS 编译器文档(尝试将其配置为“可重定位 ELF ”输出?)

  2. Linux 上的共享库必须是可重定位的,因为基地址是在运行时确定的。生成与位置无关的代码是理想的,因为它减少了重新定位库所需的工作量。如果您的库不可重定位,它将崩溃(换句话说,在创建动态库之前不要从目标文件中删除重定位信息)。在选择基地址并重新定位内部引用后,符号在运行时被解析为地址。

  3. 对于静态库,所有符号解析、重定位和加载地址分配都在编译时发生。

我唯一的猜测是,你的编译器输出的代码在运行时是不可重定位的。对我来说,在不破坏静态库的情况下如何发生这种情况是一个谜,不过......

如果您直接从 RVDS 生成静态库和共享库,一种选择是尝试将该静态库转换为共享库:

gcc -shared -o libfoo.so libfoo.a

如果这有帮助,那么 RVDS 的共享库链接器(或其配置)可能已损坏。

于 2011-04-02T03:22:26.037 回答
0

你知道坠机的原因吗?

如果您正在动态加载共享库(例如 via dlopen()),一种可能性是您假设库加载正常,而它没有加载,然后尝试通过空指针执行函数。

于 2009-05-20T05:23:25.167 回答