减少调试符号的占用(可执行文件膨胀到4 GB)
Reducing the footprint of debug symbols (executable is bloated to 4 GB)
所以基本问题是我构建的可执行文件大小为4GB,打开了调试符号(在75 MB到300 MB之间,没有调试符号和不同的优化级别)。我如何诊断/分析所有这些符号的来源,以及哪些符号在占用空间方面是最大的冒犯者?我发现了一些关于减少非调试可执行文件大小的问题(尽管它们并没有非常有启发性),但在这里我主要关心的是减少调试符号的混乱。可执行文件如此之大,以至于gdb需要花费大量时间来加载所有符号,这阻碍了调试。也许减少代码膨胀是最基本的任务,但我首先想知道我的4GB都花在哪里了。
通过'size——format=SysV'运行可执行文件,我得到以下输出:
section size addr
.interp 28 4194872
.note.ABI-tag 32 4194900
.note.gnu.build-id 36 4194932
.gnu.hash 714296 4194968
.dynsym 2728248 4909264
.dynstr 13214041 7637512
.gnu.version 227354 20851554
.gnu.version_r 528 21078912
.rela.dyn 37680 21079440
.rela.plt 15264 21117120
.init 26 21132384
.plt 10192 21132416
.text 25749232 21142608
.fini 9 46891840
.rodata 3089441 46891872
.eh_frame_hdr 584228 49981316
.eh_frame 2574372 50565544
.gcc_except_table 1514577 53139916
.init_array 2152 56753888
.fini_array 8 56756040
.jcr 8 56756048
.data.rel.ro 332264 56756064
.dynamic 992 57088328
.got 704 57089320
.got.plt 5112 57090048
.data 22720 57095168
.bss 1317872 57117888
.comment 44 0
.debug_aranges 2978704 0
.debug_info 278337429 0
.debug_abbrev 1557345 0
.debug_line 13416850 0
.debug_str 3620467085 0
.debug_loc 236168202 0
.debug_ranges 37473728 0
Total 4242540803
我猜我们可以看到'debug_str'占用了约3.6 GB。我不是100%知道"debug_str"是什么,但我猜它们可能是调试符号的字符串名称?所以这是不是在告诉我,我的那些符号的名字太大了?我怎样才能找出哪些问题并加以解决呢?
我想我可以用'nm'做一些事情,直接检查符号名称,但是输出是巨大的,我不确定如何最好地搜索它。有什么工具可以做这种分析吗?
使用的编译器是'c++ (GCC) 4.9.2'。我想我应该提一下我是在linux环境下工作的。
所以我根据John Zwinck的回答,通过以下方法找到了罪魁祸首。实际上,我只是按照他的建议运行"string"查看可执行文件并分析输出
strings my_executable > exec_strings.txt
然后我对输出进行排序,主要按照minddriot的方法:
cat exec_strings.txt | awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2- > exec_strings_sorted.txt
看了最长的字符串。事实上,这一切似乎都是来自某个特定库的一些疯狂的模板膨胀。然后我做了一些计数,像:
cat exec_strings.txt | wc -l
2928189
cat exec_strings.txt | grep <culprit_libname> | wc -l
1108426
查看提取的大约300万个字符串中,似乎有大约100万个来自这个库。最后,做
cat exec_strings.txt | wc -c
3659369876
cat exec_strings.txt | grep <culprit_libname> | wc -c
3601918899
很明显,这一百万个字符串都是超长的,并且构成了调试符号垃圾的很大一部分。因此,至少现在我可以专注于这个库,同时试图消除问题的根源。
我使用的一个技巧是在可执行文件上运行strings
,它将打印所有那些长(可能是由于模板)和大量(同上)调试符号名。您可以将其管道到sort | uniq -c | sort -n
并查看结果。在许多大型c++可执行文件中,您会看到这样的模式:
my_template<std::basic_string<char, traits, allocator>, std::unordered_map<std::basic_string<char, traits, allocator>, 1L>
my_template<std::basic_string<char, traits, allocator>, std::unordered_map<std::basic_string<char, traits, allocator>, 2L>
my_template<std::basic_string<char, traits, allocator>, std::unordered_map<std::basic_string<char, traits, allocator>, 3L>
你懂的。
在某些情况下,我决定简单地减少模板的数量。有时它会失控。其他时候,您可能会通过使用显式模板实例化,或编译项目的特定部分而不使用调试符号,甚至禁用RTTI(如果您不依赖dynamic_cast
或typeid
)来赢得一些东西。
我想我可以用'nm'做一些事情,直接检查符号名称,但是输出是巨大的,我不确定如何最好地搜索它。有什么工具可以做这种分析吗?
您可以执行以下命令按符号长度排序nm
的所有符号输出:
nm --no-demangle -a -P --size-sort myexecutable
| awk '{ print length, $0 }' | sort -n -s | cut -d" " -f2-
(按行长度排序文本文件,包括第一个|
之后的所有内容的空格。)这将显示最长的名字。您可以进一步将输出管道传输到c++filt -t
以获得所需的名称,这可以帮助您进行搜索。
根据您的情况,将可执行文件及其调试符号分割成单独的文件可能会很有用,这将允许您将不那么臃肿的可执行文件分发到目标环境/客户端等。,并在需要时将调试符号保存在单个位置。参见如何在构建目标之外生成gcc调试符号?
- 如何将不同的可执行文件合并到一个窗口框架中进行编码?像浏览器一样
- 如何使 windows 命令提示符在C++可执行文件上显示返回值?
- CMake:如何将库 A 链接到库 B,然后将可执行文件链接到库 A
- 如何使字符串出现在编译的二进制可执行文件的开头?
- 程序在使用 system() 启动另一个可执行文件时停止
- 如何更改使用 CPack 安装的可执行文件的名称?
- 在Linux上使用Clang / OLLVM交叉编译helloworld Windows可执行文件时的问题
- CMake:我们可以为一组不形成可执行文件或库的特定文件指定包含目录吗?
- 如何将图像传入C++可执行文件并将输出图像存储在新目录中?
- 共享库 (.so) 没有扩展名的 Linux 可执行文件之间的区别?
- 如何在 linux 可执行文件中使用静态库
- 错误:当我从"WinDbg"打开可执行文件时,找不到符号文件。默认导出 ntdll 的符号.dll
- 编译的C可执行文件被Windows defender检测为病毒
- Linux 可执行文件通过 dlopen 在emplace_back崩溃打开共享库
- 是否可以在命令行中将输入参数传递给可执行文件
- 如何在可执行文件中存储常量数组?
- 如果包含映射的静态库与可执行文件和动态库链接,静态映射(变量)是否会被多次释放?
- 可执行文件C++包括.obj,.lib和.dll吗?
- 无法在 Linux 上运行C++ ZMQ 项目的可执行文件
- 减少调试符号的占用(可执行文件膨胀到4 GB)