具有-L / usr / local / lib的大型可执行文件大小

问题描述 投票:2回答:1

我今天遇到了一个与(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++ g++ cygwin filesize
1个回答
0
投票

[导致程序膨胀的直接(也许是显而易见的)原因-当您在膨胀的方式-是它在标准中引用的所有符号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++.alibstd++中应该同时放置静态库和共享库在那里,所以仍然不会有代码膨胀。您的问题使我无从知晓这种情况可能是如何产生的。

删除/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++

© www.soinside.com 2019 - 2024. All rights reserved.