巨大的可执行文件大小 -L/usr/local/lib
Massive executable size with -L/usr/local/lib
我今天遇到了一个与 (cygwin) g++ 生成的二进制大小有关的奇怪问题。
编译使用标准库函数的 C++ 程序并将 -L/usr/local/lib
作为选项传递时,二进制大小绝对是巨大的(12MB 巨大)。
iostream
似乎对我测试过的影响最大。
我通过反复试验确定了 /usr/local/lib
中的 30MB 文件 libstdc++.a
是问题的根源。也就是说,我将 /usr/local/lib
的内容复制到一个单独的目录中,将那个目录添加到 link 路径而不是 /usr/local/lib
,然后删除文件,直到二进制大小恢复正常。
试验:
KEY:
(<group>)
`<command>` -> <size of resulting binary in bytes>
control 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
control 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>
明确说明,否则不应从 linker 搜索路径中编辑任何内容。它是否与首先搜索 /usr/local/lib
,然后隐式添加 -lstdc++
,因此可能 link 错误的库有关...?
程序膨胀的直接(也许是显而易见的)原因 - 当您 link 程序在
膨胀的方式 - 是它在标准中引用的所有符号
C++ 库静态绑定到在
存档 /usr/local/lib/libstdc++.a
。所有归档的目标文件
包含那些由 linker 提取并物理合并到
输出程序。
当你link正常方式而不是膨胀方式的程序时,相同
符号动态绑定到 DSO libstdc++.so
提供的定义
linker 位于其搜索目录之一而不是
/usr/local/lib/
。在这种情况下,没有目标代码被合并到程序中。 linker 只是注释
这样 运行time loader 在启动时会加载 libstdc++.so
进入同一进程并用 运行time 修补动态引用
地址。
Why does adding a directory with libstdc++.a to the search path increase the binary size so?
To my knowledge, nothing should be linked from the linker search path unless stated explicitly with -l
你在第二句中的陈述完全正确。但是,您没有作曲
或者查看整个 linker 命令行。 g++
- C++ 的 GNU 前端驱动程序
编译和 linkage - 正在编写 linker 的命令行,在幕后,
当编译完成并准备好执行 linkage 时。它接受任何东西
link您在自己的命令行中指定的年龄选项,将它们转换
如有必要,将它们添加到 the system linker, ld
理解的等效选项中
到一个新的命令行,然后附加到它的很多样板 linker 选项是不变的
对于 C++ 程序,最后将这个新命令行传递给 ld
以执行 linkage。
(这在某种程度上简化了,但本质上会发生什么)
如果您必须在命令提示符下 ld
自己调用 link C++ 程序,您
每次都必须自己输入所有样板文件,而且永远无法
记住这一切。如果您想查看所有内容,请添加 -v
(= verbose) 选项
你调用 g++
。其他 GCC 前端对其他语言执行相同的功能:gcc
for C linkages; gfortran
对于 Fortran linkages,gnat
对于 ADA linkages,等等
在 g++
默认情况下添加到 linkage 选项的所有样板文件中,您将
找到这样的:-
...
-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 系统。所以你看,如果 link 一个带有 g++
的程序,那么你 是 将 -lstdc++
传递给
默认情况下 linker。如果你没有通过它,那么任何外部引用
在您的代码中无法解析标准 C++ 库的符号,并且
linkage 将因未定义的引用而失败。
下一个问题是 linker 如何将 -lstdc++
解析为物理
搜索路径中某处的静态或共享库并使用它。
默认情况下是这样的。库选项 -lname
指示 linker 进行搜索,
首先在指定的 -Ldir
目录中,按照它们的命令行顺序,然后
在其默认搜索目录中,按照配置的顺序,
files libname.a
(静态库)或 libname.so
(动态库)。如果
当它找到其中一个时,它会停止搜索并将该文件输入到
link年龄。如果它在同一个搜索目录中找到它们,那么它会选择
libname.so
。如果它输入一个共享库然后它执行动态符号绑定
使用库,它不会向程序添加任何目标文件。如果它
输入一个静态库然后它执行静态符号绑定,确实添加对象
文件到程序。
如果您想知道 linker 的默认搜索目录 - 它在哪里
将在耗尽 -L
目录后进行搜索 - 以及它们的顺序
您需要 运行 ld
本身和 -verbose
选项。你可以这样做
将 -Wl,-verbose
传递给前端。
在 -verbose
linker 输出的开始附近,您会发现类似的内容:
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");
这是linker的内置目录搜索顺序。注意它包含
/usr/local/lib
。所以你永远不需要指定 -L/usr/local/lib
(或任何这些目录)在前端命令行中,对于 g++
或任何
其他前端,除非您想更改目录搜索顺序。
-Ldir
选项在linker命令行中出现的位置
-lname
选项无关紧要。所有 -Ldir
选项适用于所有
-lname
个选项。但是 -Ldir
选项出现的顺序
彼此 很重要,-lname
选项也是如此。
如果您link您的程序没有不必要的link年龄选项:
g++ -Wall test.cpp
link 读者将搜索满足 -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
。
动态符号绑定完成。没有代码膨胀。
但是如果你link你的程序像:
g++ -Wall test.cpp -L/usr/local/lib`
当 /usr/local/lib/libstdc++.a
存在并且 /usr/local/lib/libstdc++.so
没有,则先查找/usr/local/lib/
; libstdc++.a
一个人被发现
那里是静态 linked。有代码膨胀。
这种情况是不正常的,因为常规和熟练的安装
libstd++
到 /usr/local/lib
应该同时放置静态库和共享库
在那里,所以仍然不会有代码膨胀。你的问题让我没有洞察力
了解这种情况是如何发生的。
当你删除/usr/local/lib/libstdc++.a
你发现程序大小
恢复正常。那是因为在没有那个文件的情况下,第一个
库满足 -lstdc++
linker 找到的又是它的常态
libstdc++.so
.
您在一个仅引用 <vector>
的程序中观察到更少的膨胀
设施比一个引用 <iostream>
设施。那是因为
与 <iostream>
相比,<vector>
设施将更少的库代码拉入静态 linkage
在评论中你想知道为什么 -shared-libgcc
选项的存在
不会阻止 link/usr/local/lib/libstdc++.a
的年龄。这是因为 libgcc
不是 libstdc++
,
-shared-libgcc
只需要 link 年龄 libgcc.so
我今天遇到了一个与 (cygwin) g++ 生成的二进制大小有关的奇怪问题。
编译使用标准库函数的 C++ 程序并将 -L/usr/local/lib
作为选项传递时,二进制大小绝对是巨大的(12MB 巨大)。
iostream
似乎对我测试过的影响最大。
我通过反复试验确定了 /usr/local/lib
中的 30MB 文件 libstdc++.a
是问题的根源。也就是说,我将 /usr/local/lib
的内容复制到一个单独的目录中,将那个目录添加到 link 路径而不是 /usr/local/lib
,然后删除文件,直到二进制大小恢复正常。
试验:
KEY:
(<group>)
`<command>` -> <size of resulting binary in bytes>
control 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
control 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>
明确说明,否则不应从 linker 搜索路径中编辑任何内容。它是否与首先搜索 /usr/local/lib
,然后隐式添加 -lstdc++
,因此可能 link 错误的库有关...?
程序膨胀的直接(也许是显而易见的)原因 - 当您 link 程序在
膨胀的方式 - 是它在标准中引用的所有符号
C++ 库静态绑定到在
存档 /usr/local/lib/libstdc++.a
。所有归档的目标文件
包含那些由 linker 提取并物理合并到
输出程序。
当你link正常方式而不是膨胀方式的程序时,相同
符号动态绑定到 DSO libstdc++.so
提供的定义
linker 位于其搜索目录之一而不是
/usr/local/lib/
。在这种情况下,没有目标代码被合并到程序中。 linker 只是注释
这样 运行time loader 在启动时会加载 libstdc++.so
进入同一进程并用 运行time 修补动态引用
地址。
Why does adding a directory with libstdc++.a to the search path increase the binary size so? To my knowledge, nothing should be linked from the linker search path unless stated explicitly with -l
你在第二句中的陈述完全正确。但是,您没有作曲
或者查看整个 linker 命令行。 g++
- C++ 的 GNU 前端驱动程序
编译和 linkage - 正在编写 linker 的命令行,在幕后,
当编译完成并准备好执行 linkage 时。它接受任何东西
link您在自己的命令行中指定的年龄选项,将它们转换
如有必要,将它们添加到 the system linker, ld
理解的等效选项中
到一个新的命令行,然后附加到它的很多样板 linker 选项是不变的
对于 C++ 程序,最后将这个新命令行传递给 ld
以执行 linkage。
(这在某种程度上简化了,但本质上会发生什么)
如果您必须在命令提示符下 ld
自己调用 link C++ 程序,您
每次都必须自己输入所有样板文件,而且永远无法
记住这一切。如果您想查看所有内容,请添加 -v
(= verbose) 选项
你调用 g++
。其他 GCC 前端对其他语言执行相同的功能:gcc
for C linkages; gfortran
对于 Fortran linkages,gnat
对于 ADA linkages,等等
在 g++
默认情况下添加到 linkage 选项的所有样板文件中,您将
找到这样的:-
...
-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 系统。所以你看,如果 link 一个带有 g++
的程序,那么你 是 将 -lstdc++
传递给
默认情况下 linker。如果你没有通过它,那么任何外部引用
在您的代码中无法解析标准 C++ 库的符号,并且
linkage 将因未定义的引用而失败。
下一个问题是 linker 如何将 -lstdc++
解析为物理
搜索路径中某处的静态或共享库并使用它。
默认情况下是这样的。库选项 -lname
指示 linker 进行搜索,
首先在指定的 -Ldir
目录中,按照它们的命令行顺序,然后
在其默认搜索目录中,按照配置的顺序,
files libname.a
(静态库)或 libname.so
(动态库)。如果
当它找到其中一个时,它会停止搜索并将该文件输入到
link年龄。如果它在同一个搜索目录中找到它们,那么它会选择
libname.so
。如果它输入一个共享库然后它执行动态符号绑定
使用库,它不会向程序添加任何目标文件。如果它
输入一个静态库然后它执行静态符号绑定,确实添加对象
文件到程序。
如果您想知道 linker 的默认搜索目录 - 它在哪里
将在耗尽 -L
目录后进行搜索 - 以及它们的顺序
您需要 运行 ld
本身和 -verbose
选项。你可以这样做
将 -Wl,-verbose
传递给前端。
在 -verbose
linker 输出的开始附近,您会发现类似的内容:
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");
这是linker的内置目录搜索顺序。注意它包含
/usr/local/lib
。所以你永远不需要指定 -L/usr/local/lib
(或任何这些目录)在前端命令行中,对于 g++
或任何
其他前端,除非您想更改目录搜索顺序。
-Ldir
选项在linker命令行中出现的位置
-lname
选项无关紧要。所有 -Ldir
选项适用于所有
-lname
个选项。但是 -Ldir
选项出现的顺序
彼此 很重要,-lname
选项也是如此。
如果您link您的程序没有不必要的link年龄选项:
g++ -Wall test.cpp
link 读者将搜索满足 -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
。
动态符号绑定完成。没有代码膨胀。
但是如果你link你的程序像:
g++ -Wall test.cpp -L/usr/local/lib`
当 /usr/local/lib/libstdc++.a
存在并且 /usr/local/lib/libstdc++.so
没有,则先查找/usr/local/lib/
; libstdc++.a
一个人被发现
那里是静态 linked。有代码膨胀。
这种情况是不正常的,因为常规和熟练的安装
libstd++
到 /usr/local/lib
应该同时放置静态库和共享库
在那里,所以仍然不会有代码膨胀。你的问题让我没有洞察力
了解这种情况是如何发生的。
当你删除/usr/local/lib/libstdc++.a
你发现程序大小
恢复正常。那是因为在没有那个文件的情况下,第一个
库满足 -lstdc++
linker 找到的又是它的常态
libstdc++.so
.
您在一个仅引用 <vector>
的程序中观察到更少的膨胀
设施比一个引用 <iostream>
设施。那是因为
与 <iostream>
<vector>
设施将更少的库代码拉入静态 linkage
在评论中你想知道为什么 -shared-libgcc
选项的存在
不会阻止 link/usr/local/lib/libstdc++.a
的年龄。这是因为 libgcc
不是 libstdc++
,
-shared-libgcc
只需要 link 年龄 libgcc.so