我该如何将标准库静态链接到我的C++程序中?

26

我正在使用Code::Blocks IDE(v13.12)和GNU GCC编译器。

  1. 我想要链接程序所需的运行时库的静态版本,如何实现?
  2. 我已经知道我的可执行文件大小会增加。请告诉我其他的缺点。
  3. 在Visual C++ Express中这样做怎么样?

2
你为什么想要静态链接? - SwiftMango
6
拥有一个单独的可执行文件是常见原因。 - Cheers and hth. - Alf
4
@texasbruce,为什么您认为这是一种不良的实践方式?当然,有赞成和反对的理由,但并不到排除一种有效方法的程度。 - Mark Ransom
3
从性能方面来说不好?就性能而言,静态链接应该更快! - Mooing Duck
2
如果一个动态库已经被另一个程序加载到内存中,那么新程序的加载时间几乎为零。 - SwiftMango
显示剩余12条评论
2个回答

59

由于还没有其他人给出答案,我来试着回答一下。不幸的是,我不熟悉Code::Blocks IDE,所以我的答案只能是部分的。

1 如何使用GCC创建静态链接可执行文件

这并不是特定于IDE的,而是适用于GCC(以及许多其他编译器)的通用方法。假设您有一个简单的“hello, world”程序在main.cpp中(除了标准库和运行时库之外没有外部依赖项)。您可以通过以下方式进行编译和静态链接:

  1. Compile main.cpp to main.o (the output file name is implicit):

    $ g++ -c -Wall main.cpp
    

    The -c tells GCC to stop after the compilation step (not run the linker). The -Wall turns on most diagnostic messages. If novice programmers would use it more often and pay more attention to it, many questions on this site would not have been asked. ;-)

  2. Link main.o (could list more than one object file) statically pulling in the standard and runtime library and put the executable in the file main:

    $ g++ -o main main.o -static
    

    Without using the -o main switch, GCC would have put the final executable in the not so well-named file a.out (which once eventually stood for “assembly output”).

特别是在开始阶段,我强烈建议手动完成这些操作,因为这将有助于更好地理解构建工具链。
事实上,上述两个命令可以合并为一个命令:
$ g++ -Wall -o main main.cpp -static

任何合理的 IDE 都应该有指定编译器/链接器标志的选项。

2. 静态链接的优缺点

静态链接的优点:

  • 您有一个单一的文件,可以复制到任何具有兼容架构和操作系统的机器上,并且它将正常工作,无论安装了什么版本的库。

  • 您可以在没有共享库的环境中执行程序。例如,将静态链接的CGI可执行文件放入chroot()监狱中可能有助于减少Web服务器的攻击面。

  • 由于不需要动态链接,因此程序启动可能更快。 (我确信在其他进程已经加载共享库的情况下,相反的情况也是存在的。)

  • 由于链接器可以硬编码函数地址,因此函数调用可能更快。

  • 对于安装了多个通用库(例如LAPACK)版本的系统,静态链接可以帮助确保始终使用特定版本,而不必担心正确设置LD_LIBRARY_PATH。显然,这也是一个缺点,因为现在您不能再选择库而不重新编译。 如果您始终想要相同的版本,为什么首先会安装多个版本呢?

反对静态链接的原因

  • 正如你已经提到的,可执行文件的大小可能会急剧增长。这当然严重依赖于你链接了哪些库。

  • 如果多个进程同时需要同一个共享库,则操作系统可能足够聪明,只将共享库的文本部分加载到RAM一次。通过静态链接,你会失去这种优势,系统可能更快地耗尽内存。

  • 你的程序不再受益于库的升级。系统管理员不再仅仅用一个(希望ABI兼容的)新版本替换一个共享库,而是必须重新编译并重新安装使用它的每个程序。在我看来,这是最严重的缺点。

    例如,考虑OpenSSL库。今年早些时候发现并修复了Heartbleed漏洞时,系统管理员可以安装修补后的OpenSSL版本,并重新启动所有服务以在一天内修复漏洞。也就是说,如果他们的服务与OpenSSL动态链接。对于那些被静态链接的服务,需要几周时间才能全部修复,我非常确定,甚至今天还有一些专有的“全能软件”没有得到修复。

  • 用户无法即时替换共享库。例如,torsocks脚本(和相关库)允许用户通过适当设置LD_PRELOAD来替换网络系统库,从而将其流量路由到Tor网络。这甚至适用于开发者从未考虑过这种可能性的程序。(无论这是否安全以及是否是一个好主意,都是另一个不相关的辩论。)另一个常见的用例是通过使用专门版本替换malloc等来调试或“加固”应用程序。

在我看来,静态链接的缺点除了极特殊的情况外,优势并不明显。一个经验法则是:如果可以动态链接就动态链接,必须静态链接时才静态链接。
补充说明:
正如Alf所指出的(请参见评论),有一种特殊的GCC选项,可以选择静态链接C++标准库,但不会将整个程序静态链接。从GCC手册中得知:

-static-libstdc++

当使用g++程序链接C++程序时,通常会自动链接到libstdc++。如果libstdc++可用作共享库,并且未使用-static选项,则链接到libstdc++的共享版本。这通常是可以的。然而,有时候需要冻结程序使用的libstdc++版本,而不必完全静态链接。 -static-libstdc++选项指示g++驱动程序在不必静态链接其他库的情况下静态链接libstdc++。


1
总的来说,这是一个很好的回答。我只是想知道-static对于C++程序是否足够?或者还有其他选项可以实现C++标准库吗? - Cheers and hth. - Alf
@Cheersandhth.-Alf 不太确定你的意思。echo 'int main() {}' > main.cpp && gcc main.cpp -static && ldd a.out 显示没有动态链接。(与不使用 -static 进行比较。)或者,尝试使用一个简单的“hello, world”程序(在一个空目录中)链接和不链接 -static,并使用 su --command="chroot $PWD ./a.out"。这是你的意思吗? - 5gon12eder
3
只是询问。我依稀记得C++标准库实现有一些单独的选项。结果发现是“-static-libstdc ++”,它允许您在不静态链接基本运行时库的情况下进行静态链接。文档请参见(https://gcc.gnu.org/onlinedocs/gcc-4.9.1/gcc/Link-Options.html#Link-Options)。 - Cheers and hth. - Alf
@Cheersandhth.-Alf 很高兴知道这个信息;我之前并不知道。另一方面,我还没有遇到过需要静态链接标准库但不链接运行时和其他库的用例。但是谁知道未来会有什么需求呢。 - 5gon12eder
2
我想再添加一个优点:静态链接可能会(通常)减少可执行文件和库的总大小,因为未使用的库函数将不会被包含在内。如果您想要减小安装程序的大小并需要将库与其一起发布,那么这是您可能需要的东西。 - galinette
抱歉打扰了,但我想澄清一下关于静态链接的第二点。这个问题是否也适用于可执行二进制文件中的文本或只读部分呢? - tambel

4
在Visual C++中,/MT选项进行静态链接,/MD选项进行动态链接。(请参见http://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx)
我建议使用/MD并重新分发可从Microsoft免费获取的C ++运行时。一旦安装了C ++运行时,任何需要运行时的程序都将继续工作。您需要传递正确的选项告诉编译器使用哪个运行时。这里有一个很好的解释,Should I compile with /MD or /MT? 在Linux上,我建议重新分发libstdc++而不是静态链接。如果他们的系统libstdc++有效,则应该让用户只使用它。系统库(例如libpthread和libgcc)应该使用系统默认值。这需要在具有与您要分发的所有Linux版本兼容符号的系统上编译程序。
在Mac OS X上,只需重新分发带有对libstdc++的动态链接的应用程序。使用相同OS版本的任何人都应该能够使用您的程序。

这篇博客中的作者提到了一些与我关于不链接系统库的说法相一致的观点。http://micro.nicholaswilson.me.uk/post/31855915892/rules-of-static-linking-libstdc-libc-libgcc - Juan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接