我今天遇到了一个与(cygwin)g ++产生的二进制大小有关的怪异问题。
[编译使用标准库函数的C ++程序并传递-L/usr/local/lib
作为选项时,二进制文件的大小绝对很大(12MB很大)。
iostream
似乎对我的测试影响最大。
通过反复试验,我已将libstdc++.a
中的30MB文件/usr/local/lib
确定为问题的根源。也就是说,我将/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 / gcc中复制,考虑到问题文件名为libstdc ++,这很有意义。
如果您需要更多试用,请告诉我。
我的问题是:为什么?为什么在搜索路径中添加带有libstdc++.a
的目录会增加二进制大小?据我所知,除非使用-l<library>
明确声明,否则不应从链接程序搜索路径链接任何内容。它与首先搜索/usr/local/lib
并隐式添加-lstdc++
有关,因此也许链接了错误的库...?
[导致程序膨胀的直接(也许是显而易见的)原因-当您在膨胀的方式-是它在标准中引用的所有符号C ++库静态绑定到存档/usr/local/lib/libstdc++.a
。所有已归档的目标文件包含那些定义,这些定义由链接器提取并物理合并为输出程序。
当您以正常方式而不是the肿的方式链接程序时,符号动态绑定到DSO libstdc++.so
提供的定义链接器位于其搜索目录之一之外的位置/usr/local/lib/
。在这种情况下,没有目标代码被合并到程序中。链接器仅注释它,以便运行时加载程序在启动时将加载该libstdc++.so
进入相同的过程,并用它们的运行时修补动态引用地址。
为什么将带有libstdc ++。a的目录添加到搜索路径会增加二进制文件的大小呢?据我所知,除非使用-l
明确声明,否则不应从链接程序搜索路径链接任何内容。
您在第二句中的陈述严格正确。但是,您不是在作曲或查看整个链接器命令行。 g++
-用于C ++的GNU前端驱动程序编译和链接-正在幕后组成链接器的命令行,当编译完成并准备进行链接时。它接受任何您在自己的命令行中指定的链接选项,将其转换如有必要,将其添加到the system linker, ld
理解的等效选项中,然后将其添加到新的命令行,然后在其中附加许多不变的样板链接器选项C ++程序,最后将此新命令行传递给ld
以执行链接。(这有点简化,但是本质上会发生什么)
如果您必须自己调用ld
才能在命令提示符下链接C ++程序,则您每次都必须自己键入所有样板,并且永远无法记住这一切。如果您想查看所有内容,请在出现以下情况时添加ld
(= verbose)选项您调用-v
。其他GCC前端对其他语言执行相同的功能:g++
用于C链接;gcc
对于Fortran链接,gfortran
对于ADA链接,等等。
默认情况下,在gnat
会添加到链接选项的所有样板中,找到这样的:-
g++
来自我自己的Ubuntu 19.10系统。因此,您看到,如果用...
-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++
...
链接程序,则are传递g++
到默认情况下,链接器。如果没有通过,那么将进行任何外部引用在您的代码中无法解析标准C ++库的符号,并且对于未定义的引用,链接将失败。
下一个问题是链接器如何将-lstdc++
解析为物理静态或共享库在其搜索路径中的某处并使用它。
默认情况下,它是这样的。库选项-lstdc++
指导链接器进行搜索,首先在指定的-lname
目录中,按照它们的命令行顺序,然后在其默认搜索目录中,按照其配置顺序,文件-Ldir
(静态库)或libname.a
(动态库)。如果当找到其中一个时,它将停止搜索并将该文件输入到链接。如果它在同一搜索目录中找到它们两者,则选择libname.so
。如果输入共享库,则执行动态符号绑定与库一起使用,该库不会将任何目标文件添加到程序中。如果它输入一个静态库,然后执行静态符号绑定,does添加对象文件添加到程序中。
如果您想知道链接器的默认搜索目录-在用尽libname.so
目录后将进行搜索-它们按什么顺序进入您需要使用-L
选项运行ld
本身。你可以这样做将-verbose
传递到前端。
在-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
选项相对于each other很重要,-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>
]相比,<vector>
功能将更少的库代码提取到静态链接中
在注释中,您想知道为什么<iostream>
选项的存在确实会不阻止与-shared-libgcc
的链接。这是因为/usr/local/lib/libstdc++.a
不是libgcc
,而libgcc
只需要链接libstdc++