带有 -L/usr/local/lib 的巨大可执行文件大小
Posted
技术标签:
【中文标题】带有 -L/usr/local/lib 的巨大可执行文件大小【英文标题】:Massive executable size with -L/usr/local/lib 【发布时间】:2020-05-08 22:27:44 【问题描述】:我今天遇到了一个与 (cygwin) g++ 生成的二进制大小有关的奇怪问题。
当编译使用标准库函数并传递-L/usr/local/lib
作为选项的 C++ 程序时,二进制文件大小绝对是巨大的(12MB 巨大)。
iostream
似乎对我的测试产生了最大的影响。
我通过反复试验确定/usr/local/lib
中的30MB 文件libstdc++.a
是问题的根源。也就是说,我将/usr/local/lib
的内容复制到了一个单独的目录中,将其添加到链接路径而不是/usr/local/lib
,并删除了文件,直到二进制大小恢复正常。
试验:
KEY:
(<group>)
`<command>` -> <size of resulting binary in bytes>
控制 1(字面上没有)[无效果]:
int main()
(control)
`g++ -Wall test.cpp` -> 159,574
`g++ -Wall -Os test.cpp` -> 159,610
`g++ -Wall -Os -s test.cpp` -> 8,704
(test)
`g++ -Wall test.cpp -L/usr/local/lib` -> 159,574
`g++ -Wall -Os test.cpp -L/usr/local/lib` -> 159,610
`g++ -Wall -Os -s test.cpp -L/usr/local/lib` -> 8,704
控件 2(动态使用其他库 - 例如 stb_image...)[无效果]:
#include "stb_image.h"
int main()
int width, height, channels;
unsigned char *data = stbi_load("image.jpg", &width, &height, &channels, 0);
(control)
`g++ -Wall test.cpp stb_image.so` -> 159,944
`g++ -Wall -Os test.cpp stb_image.so` -> 159,980
`g++ -Wall -Os -s test.cpp stb_image.so` -> 8,704
(test)
`g++ -Wall test.cpp -L/usr/local/lib stb_image.so` -> 159,944
`g++ -Wall -Os test.cpp -L/usr/local/lib stb_image.so` -> 159,980
`g++ -Wall -Os -s test.cpp -L/usr/local/lib stb_image.so` -> 8,704
矢量(模板)[轻微效果]:
#include <vector>
int main()
std::vector<int> v;
v.push_back(2);
(control)
`g++ -Wall test.cpp` -> 190,228
`g++ -Wall -Os test.cpp` -> 160,429
`g++ -Wall -Os -s test.cpp` -> 8,704
(test)
`g++ -Wall test.cpp -L/usr/local/lib` -> 1,985,106
`g++ -Wall -Os test.cpp -L/usr/local/lib` -> 906,760
`g++ -Wall -Os -s test.cpp -L/usr/local/lib` -> 72,192
iostream [主要作用]:
#include <iostream>
int main()
std::cout << "iostream" << std::endl;
(control)
`g++ -Wall test.cpp` -> 161,829
`g++ -Wall -Os test.cpp` -> 161,393
`g++ -Wall -Os -s test.cpp` -> 8,704
(test)
`g++ -Wall test.cpp -L/usr/local/lib` -> 11,899,614
`g++ -Wall -Os test.cpp -L/usr/local/lib` -> 11,899,344
`g++ -Wall -Os -s test.cpp -L/usr/local/lib` -> 828,416
无法在 C w/gcc 中复制,我猜这是有道理的,因为问题文件名为 libstdc++。
如果您需要更多试验,请告诉我。
我的问题是:为什么?为什么将带有libstdc++.a
的目录添加到搜索路径会增加二进制大小?据我所知,除非使用-l<library>
明确说明,否则不应从链接器搜索路径链接任何内容。是否与首先搜索 /usr/local/lib
并隐式添加 -lstdc++
有关,因此可能链接错误的库...?
【问题讨论】:
g++ -v
应该让您更好地了解它在做什么。我的猜测是它将代码从libstdc++.a
(目标文件的存档)加载到您的可执行文件中,而不是从 DLL 动态加载。
@KeithThompson 在使用-v
时,/usr/local/lib
似乎出现在所有其他链接器搜索目录之前,这可以解释它更喜欢该目录中的 libstdc++ ......然而我觉得更奇怪的是-shared-libgcc
是否显示为在COLLECT_GCC_OPTIONS
中设置...这不会阻止静态链接 libstdc++ 吗?还是不识别/usr/local/lib
中的那个?
【参考方案1】:
程序膨胀的直接(也许是显而易见的)原因 - 当您将程序链接到
膨胀的方式 - 它在标准中引用的所有符号
C++ 库静态绑定到
存档/usr/local/lib/libstdc++.a
。所有归档的目标文件
包含那些由链接器提取并物理合并到
输出程序。
当您以正常方式而不是膨胀方式链接程序时,同样
符号动态绑定到 DSO libstdc++.so
提供的定义
链接器位于其搜索目录之一中,而不是
/usr/local/lib/
。在这种情况下,没有目标代码被合并到程序中。链接器仅注释
以便运行时加载程序在启动它时加载libstdc++.so
进入同一个进程并使用它们的运行时修补动态引用
地址。
为什么将带有 libstdc++.a 的目录添加到搜索路径会增加二进制大小? 据我所知,除非使用 -l
明确说明,否则不应从链接器搜索路径链接任何内容
您在第二句话中的陈述完全正确。然而,你不是在作曲
或查看整个链接器命令行。 g++
- C++ 的 GNU 前端驱动程序
编译和链接 - 在幕后组成链接器的命令行,
当编译完成并准备好进行链接时。它接受任何
您在自己的命令行中指定的链接选项,将它们转换
如有必要,添加到 the system linker, ld
理解的等效选项中,添加它们
到一个新的命令行,然后向它附加许多不变的样板链接器选项
对于 C++ 程序,最后将这个新命令行传递给 ld
以执行链接。
(这有点简化,但本质上会发生什么)
如果您必须自己调用 ld
在命令提示符下链接 C++ 程序,您
每次都必须自己输入所有样板,并且永远无法
记住这一切。如果您想查看所有内容,请添加 -v
( = verbose) 选项
你调用g++
。其他 GCC 前端对其他语言执行相同的功能:gcc
用于 C 链接; gfortran
Fortran 链接,gnat
ADA 链接等
在g++
默认添加到链接选项的所有样板中,您将
找到类似的:-
...
-L/usr/lib/gcc/x86_64-linux-gnu/9 \
-L/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu \
-L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib \
-L/lib/x86_64-linux-gnu \
-L/lib/../lib \
-L/usr/lib/x86_64-linux-gnu \
-L/usr/lib/../lib
-L/usr/lib/gcc/x86_64-linux-gnu/9/../../.. \
...
-lstdc++
...
那来自我自己的 Ubuntu 19.10 系统。所以你看,如果用g++
链接一个程序,那么你正在将-lstdc++
传递给
默认情况下链接器。如果你没有通过它,那么任何外部参考
在您的代码中无法解析标准 C++ 库的符号,并且
对于未定义的引用,链接将失败。
下一个问题是链接器如何将-lstdc++
解析为物理
搜索路径中某处的静态或共享库并使用它。
默认情况下是这样的。库选项-lname
指示链接器进行搜索,
首先在指定的-Ldir
目录中,按照它们的命令行顺序,然后
在其默认搜索目录中,按照配置的顺序,为任何一个
文件libname.a
(静态库)或libname.so
(动态库)。如果
当它找到其中任何一个时,它会停止搜索并将该文件输入到
联动。如果它在同一个搜索目录中找到它们,那么它会选择
libname.so
。如果它输入一个共享库,那么它会执行动态符号绑定
使用库,它不会将任何目标文件添加到程序中。如果它
输入一个静态库,然后执行静态符号绑定,确实添加对象
文件到程序中。
如果您想知道链接器的默认搜索目录 - 它在哪里
将在用尽 -L
目录后搜索 - 以及它们的顺序
您需要使用 -verbose
选项运行 ld
本身。你可以这样做
将-Wl,-verbose
传递到前端。
在-verbose
链接器输出的开头附近,您会发现类似的内容:
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); \
SEARCH_DIR("=/lib/x86_64-linux-gnu"); \
SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); \
SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); \
SEARCH_DIR("=/usr/local/lib64"); \
SEARCH_DIR("=/lib64"); \
SEARCH_DIR("=/usr/lib64"); \
SEARCH_DIR("=/usr/local/lib"); \
SEARCH_DIR("=/lib"); \
SEARCH_DIR("=/usr/lib"); \
SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); \
SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
这是链接器的内置目录搜索顺序。注意它包含
/usr/local/lib
。因此,您无需指定-L/usr/local/lib
(或任何这些目录)在前端命令行中,用于g++
或任何
其他前端,除非您想更改目录搜索顺序。
-Ldir
选项出现在链接器命令行中的相关位置
到-lname
选项无关紧要。所有-Ldir
选项均适用于所有
-lname
选项。但是-Ldir
选项出现的顺序相对于
彼此很重要,-lname
选项也是如此。
如果您链接程序时没有不必要的链接选项:
g++ -Wall test.cpp
链接器将搜索满足-lstdc++
的物理库。
在我的系统上,它要搜索的第一个目录是/usr/lib/gcc/x86_64-linux-gnu/9
,
它会在那里找到:
$ ls /usr/lib/gcc/x86_64-linux-gnu/9/libstdc++.*
/usr/lib/gcc/x86_64-linux-gnu/9/libstdc++.a /usr/lib/gcc/x86_64-linux-gnu/9/libstdc++.so
所以它可以选择libstdc++.a
和libstdc++.so
,它选择libstdc++.so
。
动态符号绑定完成。没有代码膨胀。
但是如果你像这样链接你的程序:
g++ -Wall test.cpp -L/usr/local/lib`
当/usr/local/lib/libstdc++.a
存在且/usr/local/lib/libstdc++.so
没有,则首先搜索/usr/local/lib/
;单独找到libstdc++.a
在那里并且是静态链接的。有代码膨胀。
这种情况是不正常的,因为传统且熟练的安装
libstd++
中的 /usr/local/lib
应该同时放置静态库和共享库
在那里,所以仍然没有代码膨胀。你的问题没有给我任何见解
了解这种情况是如何产生的。
当你删除/usr/local/lib/libstdc++.a
你发现程序大小
恢复正常。那是因为在没有那个文件的情况下,第一个
库满足-lstdc++
链接器发现又是它的平常
libstdc++.so
.
您在仅引用 <vector>
的程序中观察到的膨胀要少得多
设施比一个引用<iostream>
设施。那是因为
与<iostream>
相比,<vector>
工具将库代码拉入静态链接的次数要少得多
在评论中你想知道为什么-shared-libgcc
选项的存在
不妨碍与/usr/local/lib/libstdc++.a
的联动。这是因为libgcc
不是libstdc++
,
而-shared-libgcc
只需要libgcc.so
的链接
【讨论】:
感谢您的回答;尽管/usr/local/lib
从未出现在-Wl,-verbose
的输出中,但我确实觉得很奇怪...事实上,SEARCH_DIR(x)
只出现了三次。这可能是 Cygwin 的怪癖吗?
嗨。顺便说一句,我也很惊讶,但我不是 Cygwin 专家,我想关注您的系统以评论其工具链的细节。但是,我敢打赌, /usr/local/lib 无论如何都是默认的链接器搜索目录。这是类 unix 操作系统的一个非常基本的约定。以上是关于带有 -L/usr/local/lib 的巨大可执行文件大小的主要内容,如果未能解决你的问题,请参考以下文章
带有巨大 BufferdImage 的小型 pdf 文件结果