静态链接libstdc ++:任何陷阱?

Nic*_*son 81 c++ linux gcc static-libraries libstdc++

我需要使用GCC 4.7的libstdc ++将构建在Ubuntu 12.10上的C++应用程序部署到运行Ubuntu 10.04的系统,该系统附带了相当旧版本的libstdc ++.

目前,-static-libstdc++ -static-libgcc正如本博客文章所建议的那样,我正在编译:静态链接libstdc ++.作者在静态编译libstdc ++时警告不要使用任何动态加载的C++代码,这是我尚未检查的内容.到目前为止,一切似乎都很顺利:我可以在Ubuntu 10.04上使用C++ 11功能,这就是我所追求的.

我注意到这篇文章是从2005年开始的,从那以后可能发生了很大变化.它的建议仍然是最新的吗?我应该注意哪些潜在的问题?

Jon*_*ely 125

那篇博文非常不准确.

据我所知,GCC的每个主要版本(即具有不同的第一或第二版本号组件的那些)都引入了C++ ABI更改.

不对.自GCC 3.4以来引入的唯一C++ ABI变化是向后兼容的,这意味着C++ ABI已经稳定了近9年.

更糟糕的是,大多数主要的Linux发行版使用GCC快照和/或修补其GCC版本,这使得几乎不可能确切地知道在分发二进制文件时您可能正在处理的GCC版本.

分发版本的GCC版本之间的差异很小,而不是ABI的变化,例如Fedora的4.6.3 20120306(Red Hat 4.6.3-2)与上游FSF 4.6.x版本的ABI兼容,几乎可以肯定与任何4.6版本兼容. x来自任何其他发行版.

在GNU/Linux上,GCC的运行时库使用ELF符号版本控制,因此可以很容易地检查对象和库所需的符号版本,如果你有一个libstdc++.so提供这些符号的符号版本就可以工作,那么修补版本是否略有不同并不重要从您的发行版的另一个版本.

但是,如果要工作,可以动态链接C++代码(或使用C++运行时支持的任何代码).

这也不是真的.

也就是说,静态链接libstdc++.a是一种选择.

如果动态加载库(使用dlopen)它可能不起作用的原因是当您(静态)链接它时,应用程序可能不需要它依赖的libstdc ++符号,因此这些符号将不会出现在您的可执行文件中.这可以通过动态链接共享库来解决libstdc++.so(如果它依赖于它,这是正确的事情.)ELF符号插入意味着可执行文件中存在的符号将由共享库使用,但其他人不会您的可执行文件中的内容可以在libstdc++.so链接到的任何地方找到.如果您的应用程序不使用dlopen,则无需关心.

另一个选项(也是我更喜欢的选项)是libstdc++.so在应用程序旁边部署更新的选项,并确保在默认系统之前找到它libstdc++.so,这可以通过强制动态链接器在正确的位置查找,或者$LD_LIBRARY_PATH在运行时使用环境变量来完成.时间,或通过RPATH在链接时设置可执行文件.我更喜欢使用,RPATH因为它不依赖于正确设置的环境以使应用程序工作.如果您链接您的应用程序'-Wl,-rpath,$ORIGIN'(注意单引号,以防止shell试图扩展$ORIGIN),那么可执行文件将有一个RPATH$ORIGIN告诉动态链接程序查找共享库在同一目录中可执行文件本身.如果你把新版本libstdc++.so放在与可执行文件相同的目录中,它将在运行时找到,问题就解决了.(另一种选择是将可执行文件放入/some/path/bin/和更新的libstdc ++.所以进入/some/path/lib/和链接'-Wl,-rpath,$ORIGIN/../lib'或相对于可执行文件的任何其他固定位置,并相对于设置RPATH $ORIGIN)

  • 这个解释,尤其是关于RPATH的解释,是光荣的. (6认同)
  • @cap,OP特别要求部署到系统libstdc ++较旧的发行版.Steam的问题在于它们捆绑了一个libstdc ++.因此它比系统版本更旧(可能是它们在捆绑它时更新,但是发行版转移到更新的版本).这可以通过将RPATH指向包含`libstdc ++.so.6`符号链接的目录来解决,该符号链接在安装时设置为指向捆绑的lib或者如果它更新则设置为系统.Red Hat DTS使​​用了更复杂的混合链接模型,但它们很难自己做. (5认同)
  • 我不得不使用'-Wl,-rpath,$ ORIGIN'(注意rpath前面的' - ').我无法编辑答案,因为编辑必须至少6个字符.... (5认同)
  • 嘿,我很抱歉,如果我不希望我的模型用于向后发送 - compat二进制文件包括"信任其他人保持libstdc ++ ABI compat"或"在运行时有条件地链接libstdc ++"......如果这样可以惹恼一些羽毛在那里,我能做什么,我的意思是没有不尊重.如果你还记得memcpy@GLIBC_2.14的戏剧,你就不会因为我的信任问题而误导我:) (4认同)
  • 在Linux上使用您的应用程序发送libstdc ++是一个糟糕的建议.谷歌为"蒸汽libstdc ++"看到了这个带来的所有戏剧.简而言之,如果你的exe加载了想要再次运行libstdc ++的外部库(比如,opengl)(比如radeon驱动程序),那些库将使用_your_ libstdc ++,因为它已经加载了,而不是它们自己的,这就是他们需要的东西.期望.所以你回到原点. (3认同)
  • 但是如果您运送_newer_,那么如果第三方库实际上需要较旧的那个,那么您会遇到同样的麻烦.现在你把你的赌注转移到libstdc ++向后兼容,这是你无法控制的,并不像他们做到的那样真实. (2认同)
  • @cap,不,因为较新的保证向后兼容,我知道我可以依靠它,因为维护它是我的工作。如果您有证据证明您的主张,请按照 https://gcc.gnu.org/bugs/ 上的说明进行操作 (2认同)
  • 你是对的,它不是libstdc ++,但你刚才表达的关于这种改变的含义的态度对我的信任问题没有帮助:) (2认同)
  • 这个答案表明我们需要 _only_ 传送 libstdc++.so,而不是 libgcc_s.so (正如 OP 所做的那样)。那正确吗?或者这将取决于各种事情? (2认同)
  • “这不是真的。自 GCC 3.4 以来引入的唯一 C++ ABI 更改是向后兼容的,这意味着 C++ ABI 已经稳定了近九年。” 这不是真的。我最近诊断出一些商业软件中的 ABI 错误,该错误归结为 libstdc++ 中的重大更改。ABI 有 _sixteen_ 个版本,包括最新版本的 GCC(10 和 11)中的更改 (2认同)

Emi*_*rke 10

Jonathan Wakely的优秀答案之一,为什么dlopen()有问题:

由于GCC 5中的新异常处理池(参见PR 64535PR 65434),如果你dlopen和dlclose一个静态链接到libstdc ++的库,你每次都会得到(池对象的)内存泄漏.因此,如果您有可能使用dlopen,静态链接libstdc ++似乎是一个非常糟糕的主意.请注意,这是一个真正的泄漏,而不是PR 65434中提到的良性泄漏.

  • 函数 `__gnu_cxx::__freeres()` 似乎至少为这个问题提供了一些帮助,因为它释放了池对象的内部缓冲区。但对我来说,相当不清楚调用此函数对于随后意外抛出的异常有何影响。 (2认同)