5

如何在 Linux/gcc 上创建与 pthreads 和 libstdc++ 静态链接的共享对象?

4

3 回答 3

8

在我按照描述的那样回答您的问题之前,我会指出,您最终要达到的目标并不完全清楚,并且您的问题可能有更好的解决方案。

也就是说 - 尝试做你描述的事情有两个主要问题:

  1. 一种是,您需要将它们分解libpthreadlibstdc++用于制作它们的目标文件。这是因为ELF 二进制文件(在 Linux 上使用)有两个级别的“运行时”库加载 - 即使可执行文件是静态链接的,加载程序也必须在执行时加载二进制文件中的静态链接库,并映射正确的内存地址。这是在动态加载(共享对象)并映射到共享内存的库的共享链接之前完成的。因此,共享对象不能与此类库静态链接,因为在加载对象时,所有静态链接库都已加载。这是与静态库和普通目标文件链接之间的一个区别——静态库不仅像任何目标文件一样粘在可执行文件中,而且还包含在加载时引用的单独表。 (我相信这与 MS-DOS 和经典 Windows.LIB文件中更简单的静态库形成对比,但这些文件可能比我记得的要多)

    当然,您实际上不必分解libpthreadand libstdc++,您可以使用构建它们时生成的目标文件。收集它们可能有点困难(查找这些库的最终 Makefile 规则引用的对象)。而且您必须ld直接使用而不是gcc/g++来链接,以避免与动态版本链接。

  2. 第二个问题是必然的。如果您执行上述操作,您肯定会拥有您要求构建的共享对象/动态库。但是,它不会很有用,因为一旦您尝试将使用这些libpthread/ libstdc++(后者是任何 C++ 程序)的常规可执行文件与此共享对象链接,它将因符号冲突而失败 - 您的静态libpthread/libstdc++对象的符号链接您的共享对象将与该可执行文件使用的标准libpthread/中的符号发生冲突libstdc++,无论它是与标准库动态链接还是静态链接。

    然后,您当然可以尝试隐藏共享库中libstdc++/libpthread使用的静态对象中的所有符号,以某种方式将它们设为私有,或者在链接时自动重命名它们,这样就不会发生冲突。然而,即使你让它工作了,你也会在运行时发现一些不希望的结果,因为libstdc++/libpthread在全局变量和结构中都保留了相当多的状态,你现在会有重复的,并且每个都不知道另一个。这将导致这些全局数据与底层操作系统状态(例如文件描述符和内存边界)之间的不一致(可能还有标准 C 库中的一些值,例如errnofor libstdc++,以及用于libpthread.

为了避免过于宽泛的解释,我要补充一点:有时可能有合理的理由希望静态链接甚至像libstdc++and 这样的基本库,即使libc最近的系统和版本变得有点困难这些库(由于与加载器和使用的特殊链接器技巧有一点耦合),这绝对是可能的- 我做了几次,并且知道其他情况下它仍然完成。 但是,在这种情况下,您需要静态链接整个可执行文件。与标准库的静态链接结合与其他对象的动态链接通常是不可行的。


编辑:我忘记提及但重要的一个问题是 C++ 特定的。不幸的是,C++ 的设计不能很好地与对象链接和加载的经典模型(在 Unix 和其他系统上使用)配合使用。这使得 C++ 中的共享库无法真正实现应有的可移植性,因为诸如类型信息和模板之类的很多东西在对象之间没有完全分离(通常在编译时与许多实际的库代码一起从头文件中获取) )。 libstdc++出于这个原因,它与 GCC 紧密耦合,并且使用一个版本编译的代码g++通常只能与libstdc++带有这个(或非常相似)版本的 from 一起使用g++. 您肯定会注意到,如果您曾经尝试使用 GCC 4 构建一个程序,并且在您的系统上使用任何由 GCC 3 构建的非平凡库,这不仅仅是libstdc++. 如果您想要这样做的原因是试图确保您的共享对象始终与特定版本链接libstdc++并且libpthread它是针对它构建的,那么这将无济于事,因为使用不同/不兼容的程序libstdc++也将使用不兼容的 C++ 编译器或版本,因此除了实际冲突g++之外,无论如何都无法与您的共享对象链接。libstdc++

如果您想知道“为什么不这样做更简单?”,一个值得深思的一般反思:C++ 与动态/共享库很好地工作(意味着跨编译器的兼容性,以及将动态库替换为具有兼容的另一个版本的能力)接口而不重建使用它的所有东西),不仅需要编译器标准化,而且在操作系统的加载器级别,对象和库文件的结构和接口以及链接器的工作都需要显着扩展,超出相对用于普通操作系统(Microsoft Windows、基于 Mach 的系统和 NeXTStep 相关系统,如 Mac OS、VMS 相关系统和一些大型机系统上用于本地构建代码的简单 Unix 经典今天。链接器和动态加载器需要了解诸如模板和类型之类的事情,在某种程度上具有小型编译器的功能,以实际使库的代码适应给它的类型 - 并且(个人主观观察这里)似乎与对本机对象格式和链接器的此类扩展相比,更高级别的中间中间代码(连同高级语言和即时编译)正在更快地流行起来,并且可能更快地被标准化。

于 2009-02-14T10:55:51.380 回答
1

您在单独的评论中提到您正在尝试将 C++ 库移植到嵌入式设备。(我在这里添加一个新答案,而不是在这里编辑我的原始答案,因为我认为其他对这个原始问题感兴趣的 StackOverflow 用户可能仍然对它的上下文中的那个答案感兴趣

  • 显然,取决于您的嵌入式系统的精简程度(我没有太多嵌入式 Linux 经验,所以我不确定最有可能的是什么),您当然可以libstdc++在其上安装共享并动态链接所有内容否则会这样做。

  • 如果动态链接libstdc++对您不利或无法在您的系统上运行(嵌入式系统有很多不同级别的人无法知道),并且您需要链接到静态libstdc++,那么正如我所说,您唯一真正的选择是使用库静态链接可执行文件和libstdc++. 您提到将库移植到嵌入式设备,但如果这是为了在您在设备上编写或构建的某些代码中使用它并且您不介意 static libstdc++,那么静态链接所有内容(也许除了可能libc)可能没问题.

  • 如果大小libstdc++是一个问题,并且您发现您的库实际上只使用其接口的一小部分,那么我仍然建议首先尝试通过仅链接您需要的部分来确定您将节省的实际空间。它可能重要或不重要,我从来没有深入研究过libstdc++,我怀疑它有很多内部依赖关系,所以虽然你肯定不需要某些接口,但你可能仍然依赖它的很大一部分,也可能不依赖它internals - 我不知道也没有尝试过,但它可能会让你大吃一惊。您可以通过将使用库的二进制文件与它的静态构建链接并libstdc++(当然不要忘记剥离二进制文件)并将生成的可执行文件的大小与 a (stripstripped) 可执行文件与库的完整 (ped) 共享对象和动态链接在一起libstdc++

    如果您发现大小差异很大,但又不想静态链接所有内容,您可以尝试libstdc++通过在不包含您知道不需要的某些部分的情况下重建它来减小大小(对于某些部分有配置时选项)它,你也可以尝试在最终创建时删除一些独立的对象libstdc++.so。有一些工具可以优化库的大小 - 搜索网络(我记得来自一家名为MontaVista的公司,但在他们的网站上没有看到它现在,还有其他一些)。

除了上面直截了当的之外,还有一些想法和建议可以考虑:

你提到你使用uClibc,我从来没有摆弄过自己(我在嵌入式编程方面的经验要原始得多,主要涉及嵌入式处理器的汇编编程和使用最少的嵌入式库进行交叉编译)。我假设你检查过这个,我知道它uClibc旨在成为一个轻量级但相当完整的标准 C 库,但不要忘记 C++ 代码几乎不独立于 C 库,g++并且libstdc++依赖于一些非常微妙的东西(我记得问题在libc某些专有的 Unix 版本上使用),所以我不会只是假设g++或者 GNU在没有尝试的情况下libstdc++实际上可以使用uClibc- 我不记得在uClibc页面中提到过它。

此外,如果这是一个嵌入式系统,请考虑其性能、计算能力、整体复杂性和时序/简单性/可靠性要求。考虑所涉及的复杂性,并考虑在您的嵌入式系统中使用 C++ 和线程是否合适,如果系统中没有其他东西使用这些,是否值得为该库引入。可能是,不知道我无法分辨的库或系统(同样,如今嵌入式系统范围如此广泛)。

在这种情况下,我偶然发现了一个快速链接uClibc——如果你在嵌入式系统上工作,使用uClibc并希望在其上使用 C++ 代码——看看uClibc++. 我不知道你需要多少标准 C++ 东西并且它已经支持,而且它似乎是一个正在进行的项目,所以不清楚它是否处于对你来说已经足够好的状态,但假设你的工作也在开发仍然,libstdc++对于您的嵌入式工作,它可能是 GCC 的一个很好的替代品。

于 2009-02-14T13:26:13.300 回答
0

我认为这个人很好地解释了为什么这没有意义。使用您的共享对象但使用不同的 libstdc++ 的 C++ 代码可以正常链接,但无法正常工作。

于 2009-02-14T11:00:56.220 回答