【问题标题】:Static library linked two times静态库链接两次
【发布时间】:2015-09-21 11:10:42
【问题描述】:

我有以下设置:

  1. 静态库
  2. 链接到 (1.) 的动态库
  3. 链接到 (1.) 和 (2.) 的可执行文件

来自静态库的代码现在被复制并出现在动态库和可执行文件中。

问题:

数据(全局变量、静态类成员)是否也重复,可执行文件和 dll 是否看到相同的数据?

Linux 和 Windows 有区别吗?

你会怎么解决这个问题?

编辑:

感谢您的回答,我现在可以准确解释我的情况发生了什么。

静态库没有导出/导入标志。 动态库有自己的符号导出。

窗户:

动态库有一个静态库的文本+数据段的副本。 可执行文件无法知道动态库已链接静态库,因为从外部看不到任何静态库符号。

Linux:

动态库拥有静态库的文本数据段的副本,并将静态库中的所有符号(文本和数据)包含在其自己的符号表中。 -> 可执行文件看到,动态库已经定义了静态库的所有符号,并没有重新定义它们。

这很糟糕,因为您通常希望在 linux 和 windows 上具有相同的行为。

  1. 共享符号(Linux 上的默认设置)
  • 在将静态库链接到共享库时,对静态库中的所有符号添加 dll 导出命令。 __attribute__ ((dllexport))
  • 在将静态库链接到可执行文件时添加 dll 导入命令。 __attribute__ ((dllimport))
  • 代码和数据仅驻留在共享库中,可从外部链接
  1. 冗余符号(Windows 默认)
  • 您需要确保静态库的符号不包含在共享库的符号表中
  • __attribute__ ((visibility ("hidden"))) 在 gcc 上
  • 链接可执行文件时,静态库中的符号无法在任何地方找到,因此会再次包含它们。

【问题讨论】:

  • 是的,数据是重复的。您可以通过使用动态库来解决它。
  • @UmNyobe:已经有一个 DLL;创建另一个将无济于事,因为它不会与第一个共享全局数据。
  • 这些库是预编译的还是作为源代码提供的?

标签: c++


【解决方案1】:

据我所知,这取决于操作系统(因为 C++ 语言并没有太多说明库应该如何工作)。

在 Windows 上,您将获得两倍的代码和数据,更糟糕的是,在该库中声明的全局变量(!)

当静态链接程序中的标准库和使用它的库时,当您获得两个默认分配器并且如果您在一个库上调用 new 并在另一个上调用 delete 时,会出现此问题,对象将泄漏在new 端,堆很可能在delete 端损坏。

我不知道其他操作系统的详细信息,但我预计可能会出现类似的问题

【讨论】:

  • 新/删除技巧+1:我过去曾遭受过这种情况,很快学会了避免使用异构构建。
  • 如果所有的库调用都是功能的(也就是说,它们没有观察或修改任何全局状态)使用静态链接是可以的,因为代码可能更多高效/小(仅包括您正在使用的符号),在所有其他情况下我建议反对它
  • 在 Linux 上似乎以某种方式没有复制数据。 stackoverflow.com/questions/4925233/…
  • @dari 我刚刚对此进行了测试,如果您将链接器配置为不从静态库中导出符号(我可以在 gist.github.com/pqnet/7cb2a61c9c88f607bb63 上付出不小的努力),您会得到类似的效果在 linux 和 mac os 中。
  • 发现此类问题的一种方法是使用 valgrind。它将报告“无效释放”,然后是分配变量的代码位置(例如全局),以及两个试图释放它的代码位置。如果您查看这两个免费库,它们很可能指向两个不同的库,这表明相同的代码存在两次,并且运行不正确。
【解决方案2】:

IMO 这是一个糟糕的情况,因为有两个可执行文件(exe 和 dll),每个都有它们的代码实例和全局数据。它们是独立构建的,不能共享它们的内存映射。

一个选项可以是让 dll 公开静态库的所需成员,以便 exe 可以直接链接到它们。

【讨论】:

  • 我理解你的答案是否正确:重新声明dll中静态库中的所有变量和函数,这样可执行文件就不必链接到静态库了。
  • 是的,这就是我的意思,希望链接器会同意。该库的标头将存在三个版本:编译静态的标准、构建 dll 的导出和 exe 的导入。
猜你喜欢
  • 1970-01-01
  • 2021-08-09
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多