[英]How to create a shared object that is statically linked with pthreads and libstdc++ on Linux/gcc?
如何在Linux / gcc上创建与pthreads和libstdc ++静态链接的共享对象?
在我回答你所描述的问题之前,我会注意到你最终想要实现的目标并不完全清楚,而且你的问题可能有更好的解决方案。
这就是说 - 尝试做你所描述的事情有两个主要问题:
一个是,您需要将libpthread
和libstdc++
分解为它们所使用的目标文件。 这是因为ELF二进制文件 (在Linux上使用)有两个级别的“运行时”库加载 - 即使可执行文件是静态链接的,加载程序也必须在执行时加载二进制文件中的静态链接库,并映射正确的内存地址。 这是在动态加载(共享对象)并映射到共享内存的库的共享链接之前完成的。 因此,共享对象不能与这些库静态链接,因为在加载对象时,已经加载了所有静态链接库。 这是链接静态库和普通对象文件之间的一个区别 - 静态库不仅像任何目标文件一样粘在可执行文件中,而且还包含加载时引用的单独表。 (我相信这与MS-DOS和经典Windows, .LIB
文件中更简单的静态库形成鲜明对比,但对于那些可能比我记忆的更多) 。
当然,您实际上不必分解libpthread
和libstdc++
,您只需使用构建它们时生成的目标文件。 收集它们可能有点困难(查找这些库的最终Makefile规则引用的对象)。 你必须直接使用ld
而不是gcc
/ g++
来链接,以避免与动态版本链接。
第二个问题是重要的。 如果您执行上述操作,您将确保拥有您要求构建的共享对象/动态库。 但是,它不会非常有用,因为一旦您尝试将使用这些libpthread
/ libstdc++
(后者是任何C ++程序)的常规可执行文件与此共享对象链接,它将失败并出现符号冲突 - 静态libpthread
的符号您链接共享对象的/ libstdc++
对象将与该可执行文件使用的标准libpthread
/ libstdc++
的符号冲突,无论它是否与标准库动态或静态链接。
当然,您可以尝试从共享库使用的libstdc++
/ libpthread
隐藏静态对象中的所有符号,以某种方式将它们设为私有,或者在链接上自动重命名它们以便不会发生冲突。 但是,即使你得到了它,你也会在运行时发现一些不可取的结果,因为libstdc++
/ libpthread
在全局变量和结构中保留了相当多的状态,你现在可以重复这些状态并且每个都不知道另一个。 这将导致这些全局数据与底层操作系统状态(如文件描述符和内存边界)之间的不一致(可能还有标准C库中的某些值,例如libstdc++
errno
,以及libpthread
信号处理程序和计时器)。
为了避免过于宽泛的解释,我将补充一句话:有时候,想要静态链接甚至像libstdc++
甚至libc
这样的基本库都是明智的理由 ,即使它对于最近的系统变得有点困难这些库的版本(由于与加载器的一些耦合和使用的特殊链接器技巧),它绝对是可能的 - 我做了几次,并且知道其他情况仍然完成。 但是 ,在这种情况下,您需要静态链接整个可执行文件 。 与标准库的静态链接与其他对象的动态链接通常是不可行的。
编辑 :我忘记提及的一个问题,但重要的是要考虑C ++特定。 遗憾的是,C ++不适用于对象链接和加载的经典模型(在Unix和其他系统上使用)。 这使得C ++中的共享库不是真正可移植的,因为很多东西如类型信息和模板在对象之间没有干净的分离(通常在编译时从头文件中获取大量实际的库代码) )。 libstdc++
与GCC紧密结合,使用一个版本的g++
编译的代码通常只能使用libstdc++
和这个(或非常相似的) g++
版本。 您肯定会注意到,如果您尝试使用GCC 4构建一个程序,并且您的系统上使用GCC 3构建了任何非平凡的库,那么这不仅仅是libstdc++
。 如果你想要这样做的原因是试图确保你的共享对象总是与它所构建的libstdc++
和libpthread
的特定版本相关联,那么这将无济于事,因为使用不同/不兼容的libstdc++
也将是使用不兼容的C ++编译器或g++
版本构建,因此无论如何都无法与您的共享对象链接, 除了实际的libstdc++
冲突 。
如果你想知道“为什么这不是更简单?”,一般的反思值得深思 :让C ++与动态/共享库很好地配合(意味着编译器之间的兼容性,以及用兼容的另一个版本替换动态库的能力)接口没有重建使用它的所有东西),不仅需要编译器标准化,而是在操作系统的加载器级别,对象和库文件的结构和接口以及链接器的工作需要显着扩展到相对的用于本机构建代码的常见操作系统( Microsoft Windows,基于Mach的系统和NeXTStep亲属,如Mac OS,VMS亲属和一些大型机系统 )上使用的简单Unix经典。 链接器和动态加载器需要知道诸如模板和键入之类的东西, 在某种程度上具有小编译器的功能,以实际使库的代码适应给它的类型 - 并且( 这里的个人主观观察 )似乎更高级别的中间代码(与更高级别的语言和即时编译一起)比本机对象格式和链接器的扩展更快地捕获并且可能更快地标准化。
您在单独的注释中提到您正在尝试将C ++库移植到嵌入式设备。 ( 我在这里添加了一个新的答案,而不是在这里编辑我的原始答案,因为我认为对这个原始问题感兴趣的其他StackOverflow用户可能仍然对其上下文中的答案感兴趣 )
显然,取决于你的嵌入式系统是如何被剥离的(我没有太多的嵌入式Linux经验,所以我不确定最有可能的是什么),你当然可以在其上安装共享libstdc++
并动态链接所有内容你不这样做。
如果与libstdc++
动态链接对您不利或者不在您的系统上工作(有许多不同级别的嵌入式系统无法知道),并且您需要链接到静态libstdc++
,那么就像我说的那样,只有你自己real选项是使用库和libstdc++
将可执行文件静态链接。 您提到将库移植到嵌入式设备,但如果这是为了在您编写或构建在设备上的某些代码中使用它而您不介意静态libstdc++
,那么静态链接所有内容(除了libc
)是可能没关系。
如果libstdc++
的大小是一个问题,并且您发现您的库实际上只使用了其一小部分接口,那么我建议首先尝试通过仅链接您需要的部分来确定您将节省的实际空间。 它可能是重要的与否,我从未深入研究libstdc++
,我怀疑它有很多内部依赖关系,所以当你肯定不需要某些接口时,你可能会或者可能不会依赖于很大一部分它的内部 - 我不知道,也没有尝试,但它可能会让你大吃一惊。 您可以通过只使用该库针对的是一个静态编译和链接二进制得到一个想法libstdc++
(不要忘记,当然脱光二进制),并比较生成的可执行文件的大小,与一个(总规模strip
ped)可执行文件与库和libstdc++
的完整( strip
ped)共享对象动态链接在一起。
如果您发现大小差异很大,但又不想静态链接所有内容,那么您尝试通过重建它来减少libstdc++
的大小,而不需要您知道不需要的某些部分(某些部分有配置时选项)在最后创建libstdc++.so
你也可以尝试删除一些独立的对象libstdc++.so
。有一些工具可以优化库的大小 - 搜索网页(我记得一个名为MontaVista的公司,但没有看到它现在,在他们的网站上,还有其他一些)。
除了上面的直截了当,还有一些想法和建议要考虑:
你提到你使用uClibc
,我从来没有摆弄过自己(我对嵌入式编程的经验更为原始,主要涉及嵌入式处理器的汇编编程和最小嵌入式库的交叉编译)。 我假设您已经检查了这一点,并且我知道uClibc
旨在成为一个轻量级但相当完整的标准C库,但不要忘记C ++代码几乎不依赖于C库,而g++
和libstdc++
依赖于一些微妙的东西(我记得在一些专有的Unix版本上使用libc
问题),所以我不会假设g++
或GNU libstdc++
实际上可以在不尝试的情况下使用uClibc
- 我不记得在uClibc
页面中看到过它。
此外,如果这是一个嵌入式系统,请考虑其性能,计算能力,总体复杂性以及时序/简单性/可靠性要求。 考虑到所涉及的复杂性,并考虑使用C ++和线程是否适合您的嵌入式系统,如果系统中没有其他任何东西使用它们,那么是否值得为该库引入。 它可能是,不知道我无法分辨的库或系统(再次,嵌入式系统现在如此广泛)。
在这种情况下,只是一个快速链接,我偶然发现了寻找uClibc
- 如果您正在使用嵌入式系统,使用uClibc
,并希望在其上使用C ++代码 - 请查看uClibc++
。 我不知道你需要多少标准C ++的东西,它已经支持了,它似乎是一个正在进行的项目,所以不清楚它是否已经处于一个足够好的状态,但假设你的工作也在对于嵌入式工作,它可能是GCC的libstdc++
一个很好的替代品。
我认为这个人很好地解释了为什么这没有意义。 使用您的共享对象但不同的libstdc ++的C ++代码可以正常链接,但不起作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.