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

我需要在Ubuntu 12.10上使用GCC 4.7的libstdc ++部署一个C ++应用程序到运行Ubuntu 10.04的系统,该系统带有一个相当老版本的libstdc ++。

目前,我正在编译-static-libstdc++ -static-libgcc ,正如本博文所build议的: 静态链接libstdc ++ 。 作者警告,静态编译libstdc ++时不要使用任何dynamic加载的C ++代码,这是我还没有检查过的东西。 不过,现在一切似乎都进展顺利:我可以使用Ubuntu 10.04上的C ++ 11function,这正是我以前的做法。

我注意到这篇文章是从2005年开始的,也许从那以后有了很大变化。 它的build议还是最新的吗? 有什么潜在的问题我应该知道?

那个博客文章相当不准确。

据我所知,在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提供这些符号就可以工作,不pipe它是轻微的与您的发行版的另一个版本不同的修补版本。

但是如果这个工作正常,那么就没有C ++代码(或者任何使用C ++运行时支持的代码)可以被dynamic链接。

这也是不正确的。

也就是说,静态链接到libstdc++.a是你的一个select。

如果dynamic加载库(使用dlopen ),它可能无法正常工作的原因是,当您(静态)链接它时,它所依赖的libstdc ++符号可能不会被您的应用程序需要,因此这些符号不会出现在您的可执行文件中。 这可以通过将共享库dynamic链接到libstdc++.so (这是正确的做法,如果依赖它的话)。ELF符号插入意味着在您的可执行文件中存在的符号将被共享库使用,但其他没有出现在你的可执行文件中的任何libstdc++.so都可以find, libstdc++.so它链接到。 如果您的应用程序不使用dlopen ,则不需要关心这一点。

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

除了Jonathan Wakely的优秀答案之外,为什么dlopen()是有问题的:

由于GCC 5中有新的exception处理池(请参阅PR 64535和PR 65434 ),如果您将静态链接到libstdc ++的库设置为dlopen和dlclose,则每次都会收到(池对象的)内存泄漏。 所以,如果有任何机会你会使用dlopen,静态链接libstdc ++似乎是一个非常糟糕的主意。 请注意,这是一个真正的泄漏,而不是PR 65434中提到的良性泄漏。

您可能还需要确保您不依赖dynamicglibc。 在生成的可执行文件上运行ldd ,并记下任何dynamic依赖关系(libc / libm / libpthread是可疑用户)。

额外的练习是使用这种方法build立一堆涉及C ++ 11的例子,并在真实的10.04系统上实际尝试得到的二进制文件。 在大多数情况下,除非你用dynamic加载做一些奇怪的事情,否则你马上就会知道程序是工作还是崩溃。