Linux 静态链接 libstdc++:有什么问题吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13636513/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Linking libstdc++ statically: any gotchas?
提问by Nick Hutchinson
I need to deploy a C++ application built on Ubuntu 12.10 with GCC 4.7's libstdc++ to systems running Ubuntu 10.04, which comes with a considerably older version of libstdc++.
我需要使用 GCC 4.7 的 libstdc++ 将在 Ubuntu 12.10 上构建的 C++ 应用程序部署到运行 Ubuntu 10.04 的系统,该系统带有相当旧的 libstdc++ 版本。
Currently, I'm compiling with -static-libstdc++ -static-libgcc
, as suggested by this blog post: Linking libstdc++ statically. The author warns against using any dynamically-loaded C++ code when compiling libstdc++ statically, which is something I haven't yet checked. Still, everything seems to be going smoothly so far: I can make use of C++11 features on Ubuntu 10.04, which is what I was after.
目前,我正在编译-static-libstdc++ -static-libgcc
,正如这篇博客文章所建议的:静态链接 libstdc++。作者警告在静态编译 libstdc++ 时不要使用任何动态加载的 C++ 代码,这是我尚未检查的内容。尽管如此,到目前为止一切似乎都很顺利:我可以在 Ubuntu 10.04 上使用 C++11 功能,这正是我所追求的。
I note that this article is from 2005, and perhaps much has changed since then. Is its advice still current? Are there any lurking issues I should be aware of?
我注意到这篇文章是 2005 年的,也许从那时起发生了很大的变化。它的建议是否仍然有效?有什么我应该注意的潜在问题吗?
采纳答案by Jonathan Wakely
That blog post is pretty inaccurate.
那篇博文很不准确。
As far as I know C++ ABI changes have been introduced with every major release of GCC (i.e. those with different first or second version number components).
据我所知,GCC 的每个主要版本(即具有不同的第一或第二版本号组件的版本)都引入了 C++ ABI 更改。
Not true. The only C++ ABI changes introduced since GCC 3.4 have been backward-compatible, meaning the C++ ABI has been stable for nearly nine years.
不对。自 GCC 3.4 以来引入的唯一 C++ ABI 更改是向后兼容的,这意味着 C++ ABI 已经稳定了近九年。
To make matters worse, most major Linux distributions use GCC snapshots and/or patch their GCC versions, making it virtually impossible to know exactly what GCC versions you might be dealing with when you distribute binaries.
更糟糕的是,大多数主要的 Linux 发行版都使用 GCC 快照和/或修补其 GCC 版本,这使得在分发二进制文件时几乎不可能确切知道您可能正在处理哪些 GCC 版本。
The differences between distributions' patched versions of GCC are minor, and not ABI changing, e.g. Fedora's 4.6.3 20120306 (Red Hat 4.6.3-2) is ABI compatible with the upstream FSF 4.6.x releases and almost certainly with any 4.6.x from any other distro.
发行版的 GCC 补丁版本之间的差异很小,并且 ABI 没有变化,例如 Fedora 的 4.6.3 20120306 (Red Hat 4.6.3-2) 与上游 FSF 4.6.x 版本的 ABI 兼容,并且几乎可以肯定与任何 4.6 兼容。 x 来自任何其他发行版。
On GNU/Linux GCC's runtime libraries use ELF symbol versioning so it's easy to check the symbol versions needed by objects and libraries, and if you have a libstdc++.so
that provides those symbols it will work, it doesn't matter if it's a slightly different patched version from another version of your distro.
在 GNU/Linux GCC 的运行时库使用 ELF 符号版本控制,因此很容易检查对象和库所需的符号版本,如果你有一个libstdc++.so
提供这些符号的它会工作,它是否是一个稍微不同的补丁版本并不重要来自另一个版本的发行版。
but no C++ code (or any code using the C++ runtime support) may be linked dynamically if this is to work.
但是,如果要这样做,则不能动态链接 C++ 代码(或任何使用 C++ 运行时支持的代码)。
This is not true either.
这也不是真的。
That said, statically linking to libstdc++.a
is one option for you.
也就是说,静态链接到libstdc++.a
是您的一种选择。
The reason it might not work if you dynamically load a library (using dlopen
) is that libstdc++ symbols it depends on might not have been needed by your application when you (statically) linked it, so those symbols will not be present in your executable. That can be solved by dynamically-linking the shared library to libstdc++.so
(which is the right thing to do anyway if it depends on it.) ELF symbol interposition means symbols that are present in your executable will be used by the shared library, but others not present in your executable will be found in whichever libstdc++.so
it links to. If your application doesn't use dlopen
you don't need to care about that.
如果您动态加载库(使用dlopen
),它可能不起作用的原因是,当您(静态)链接它时,您的应用程序可能不需要它所依赖的 libstdc++ 符号,因此这些符号将不会出现在您的可执行文件中。这可以通过将共享库动态链接到libstdc++.so
(如果依赖于它,无论如何都是正确的做法)来解决。ELF 符号插入意味着可执行文件中存在的符号将被共享库使用,但其他符号则不会存在于您的可执行文件中,可以在libstdc++.so
它链接到的任何地方找到。如果您的应用程序不使用dlopen
,则无需关心。
Another option (and the one I prefer) is to deploy the newer libstdc++.so
alongside your application and ensure it is found before the default system libstdc++.so
, which can be done by forcing the dynamic linker to look in the right place, either using $LD_LIBRARY_PATH
environment variable at run-time, or by setting an RPATH
in the executable at link-time. I prefer to use RPATH
as it doesn't rely on the environment being set correctly for the application to work. If you link your application with '-Wl,-rpath,$ORIGIN'
(note the single quotes to prevent the shell trying to expand $ORIGIN
) then the executable will have an RPATH
of $ORIGIN
which tells the dynamic linker to look for shared libraries in the same directory as the executable itself. If you put the newer libstdc++.so
in the same directory as the executable it will be found at run-time, problem solved. (Another option is to put the executable in /some/path/bin/
and the newer libstdc++.so in /some/path/lib/
and link with '-Wl,-rpath,$ORIGIN/../lib'
or any other fixed location relative to the executable, and set the RPATH relative to $ORIGIN
)
另一种选择(也是我更喜欢的选择)是将较新libstdc++.so
的应用程序与应用程序一起部署,并确保在默认系统之前找到它libstdc++.so
,这可以通过强制动态链接器查找正确的位置来完成,或者$LD_LIBRARY_PATH
在运行时使用环境变量 -时间,或通过RPATH
在链接时在可执行文件中设置。我更喜欢使用,RPATH
因为它不依赖于为应用程序正确设置的环境。如果您链接您的应用程序'-Wl,-rpath,$ORIGIN'
(注意单引号,以防止shell试图扩展$ORIGIN
),那么可执行文件将有一个RPATH
的$ORIGIN
告诉动态连接器的外观在同一个目录中可执行文件本身的共享库。如果你把较新的libstdc++.so
在与可执行文件相同的目录中,它将在运行时找到,问题已解决。(另一种选择是将可执行文件/some/path/bin/
和较新的 libstdc++.so 放入/some/path/lib/
并链接到'-Wl,-rpath,$ORIGIN/../lib'
或任何其他相对于可执行文件的固定位置,并设置相对于的 RPATH $ORIGIN
)
回答by Alexander L. Belikoff
You might also need to make sure that you don't depend on the dynamic glibc. Run ldd
on your resulting executable and note any dynamic dependencies (libc/libm/libpthread are usal suspects).
您可能还需要确保不依赖于动态 glibc。ldd
在生成的可执行文件上运行并注意任何动态依赖项(libc/libm/libpthread 是常见的嫌疑人)。
Additional exercise would be building a bunch of involved C++11 examples using this methodology and actually trying the resulting binaries on a real 10.04 system. In most cases, unless you do something weird with dynamic loading, you'll know right away whether the program works or it crashes.
额外的练习是使用这种方法构建一堆涉及的 C++11 示例,并在真正的 10.04 系统上实际尝试生成的二进制文件。在大多数情况下,除非您对动态加载做一些奇怪的事情,否则您会立即知道程序是正常工作还是崩溃。
回答by Emil Styrke
One addition to Jonathan Wakely's excellent answer, why dlopen() is problematic:
Jonathan Wakely 的优秀答案的一个补充,为什么 dlopen() 有问题:
Due to the new exception handling pool in GCC 5 (see PR 64535and PR 65434), if you dlopen and dlclose a library that is statically linked to libstdc++, you will get a memory leak (of the pool object) each time. So if there's any chance that you'll ever use dlopen, it seems like a really bad idea to statically link libstdc++. Note that this is a real leak as opposed to the benign one mentioned in PR 65434.
由于 GCC 5 中的新异常处理池(请参阅PR 64535和PR 65434),如果您 dlopen 和 dlclose 静态链接到 libstdc++ 的库,则每次都会出现内存泄漏(池对象的)。因此,如果您有机会使用 dlopen,那么静态链接 libstdc++ 似乎是一个非常糟糕的主意。请注意,这是真正的泄漏,而不是PR 65434 中提到的良性泄漏。
回答by Fedorov7890
I'd like to add to Jonathan Wakely's answer the following.
我想在 Jonathan Wakely 的回答中添加以下内容。
Playing around -static-libstdc++
on linux, I've faced the problem with dlclose()
. Suppose we have an application 'A' statically linked to libstdc++
and it loads dynamically linked to libstdc++
plugin 'P' at runtime. That's fine.
But when 'A' unloads 'P', segmentation fault occurs. My assumption is that after unloading libstdc++.so
, 'A' no longer can use symbols related to libstdc++
. Note that if both 'A' and 'P' are statically linked to libstdc++
, or if 'A' is linked dynamically and 'P' statically, the problem does not occur.
-static-libstdc++
在 linux 上玩,我遇到了dlclose()
. 假设我们有一个静态链接到的应用程序“A” libstdc++
,它libstdc++
在运行时动态链接到插件“P”。没关系。但是当“A”卸载“P”时,会发生分段错误。我的假设是卸载后libstdc++.so
,'A' 不再可以使用与libstdc++
. 请注意,如果 'A' 和 'P' 都静态链接到libstdc++
,或者如果 'A' 动态链接而 'P' 静态链接,则不会出现问题。
Summary: if your application loads/unloads plugins that may dynamically
link to libstdc++
, the app must also be linked to it dynamically.
This is just my observation and I'd like to get your comments.
总结:如果您的应用程序加载/卸载可能动态链接到 的插件libstdc++
,则应用程序也必须动态链接到它。这只是我的观察,我想得到你的意见。
回答by Jonathan McDevitt
Add-on to Jonathan Wakely's answer regarding the RPATH:
Jonathan Wakely 关于 RPATH 的回答的附加内容:
RPATH will only work if the RPATH in question is the RPATH of the running application. If you have a library which dynamically links to any library through its own RPATH, the library's RPATH will be overwritten by the RPATH of the application which loads it. This is a problem when you cannot guarantee that the RPATH of the application is the same as that of your library, e.g. if you expect your dependencies to be in a particular directory, but that directory is not part of the application's RPATH.
RPATH 仅在所讨论的 RPATH 是正在运行的应用程序的 RPATH 时才起作用。如果您有一个库通过其自己的 RPATH 动态链接到任何库,则库的 RPATH 将被加载它的应用程序的 RPATH 覆盖。当您无法保证应用程序的 RPATH 与您的库的 RPATH 相同时,这是一个问题,例如,如果您希望您的依赖项位于特定目录中,但该目录不是应用程序的 RPATH 的一部分。
For example, let us say you have an application App.exe which has a dynamically-linked dependency on libstdc++.so.x for GCC 4.9. The App.exe has this dependency resolved through the RPATH, i.e.
例如,假设您有一个应用程序 App.exe,它对 GCC 4.9 的 libstdc++.so.x 具有动态链接的依赖关系。App.exe 通过 RPATH 解决了这种依赖关系,即
App.exe (RPATH=.:./gcc4_9/libstdc++.so.x)
Now let's say there is another library Dependency.so, which has a dynamically-linked dependency on libstdc++.so.y for GCC 5.5. The dependency here is resolved through the RPATH of the library, i.e.
现在假设有另一个库 Dependency.so,它对 GCC 5.5 的 libstdc++.so.y 具有动态链接的依赖关系。这里的依赖是通过库的RPATH解决的,即
Dependency.so (RPATH=.:./gcc5_5/libstdc++.so.y)
When App.exe loads Dependency.so, it neither appends nor prepends the RPATH of the library. It doesn't consult it at all. The only RPATH which is considered will be that of the running application, or App.exe in this example. That means that if the library relies on symbols which are in gcc5_5/libstdc++.so.y but not in gcc4_9/libstdc++.so.x, then the library will fail to load.
当 App.exe 加载 Dependency.so 时,它既不会附加也不会预先添加 library 的 RPATH。它根本不咨询它。唯一被考虑的 RPATH 将是正在运行的应用程序的 RPATH,或本示例中的 App.exe。这意味着如果库依赖于 gcc5_5/libstdc++.so.y 但不在 gcc4_9/libstdc++.so.x 中的符号,则库将无法加载。
This is just as a word of warning, since I've run into these issues myself in the past. RPATH is a very useful tool but its implementation still has some gotchas.
这只是一个警告,因为我过去自己也遇到过这些问题。RPATH 是一个非常有用的工具,但它的实现仍然有一些问题。