如何减小生成的二进制文件的大小?

时间:2011-07-21 06:04:05

标签: linux gcc compiler-optimization strip

我知道有一个“-Os”选项可以“优化尺寸”,但它在某些情况下影响甚微,甚至不会增加尺寸:(

strip(或“-s”选项)删除调试符号表,工作正常;但它只能减少尺寸的小比例。

还有其他方法可以继续吗?

9 个答案:

答案 0 :(得分:13)

除了明显的(-Os -s)之外,将函数对齐到不会崩溃的最小可能值(我不知道ARM对齐要求)可能会挤出每个函数的几个字节。
-Os 已禁用对齐功能,但这可能仍默认为4或8之类的值。 ARM可以使用1,这可能会节省一些字节。

-ffast-math(或者磨损性较小的-fno-math-errno)不会设置errno并避免一些检查,这会减少代码大小。如果像大多数人一样,你还没有读过errno,那就是一个选择。

正确使用__restrict(或restrict)和const可以消除冗余负载,从而使代码更快更小(也更正确)。正确地将纯函数标记为电子函数调用。

启用LTO可能有所帮助,如果不可用,则可以一次性将所有源文件编译为二进制文件(gcc foo.c bar.c baz.c -o program,而不是编译foo.cbar.c和{{1首先对象文件然后链接)将产生类似的效果。它使得优化器一次可见一切,可能使其更好地工作。

baz.c可能是一个选项(请注意,这通常是使用任何“O”启用,但不是在嵌入式目标上)。

将静态全局变量(希望没有那么多,但仍然是)放入结构体中可以消除大量的开销来初始化它们。我在编写第一个OpenGL加载器时学到了这一点。在结构中包含所有函数指针并使用-fdelete-null-pointer-checks初始化结构会生成对= {}的一次调用,而初始化指针时,“正常方式”会生成一百KB的代码,只是为了将每一个设置为零单独

避免使用像devil这样的非平凡构造函数静态本地变量(POD类型没问题)。 Gcc将初始化非平凡构造函数静态本地线程安全,除非您使用memset进行编译,该链接在很多的额外代码中(即使您根本不使用线程)。 / p>

使用libowfat而不是普通的crt可以大大减少二进制文件大小。

答案 1 :(得分:7)

You can also use -nostartfiles和/或-nodefaultlibs-nostdlib的组合。如果您不想要标准的启动文件,则必须编写自己的_start函数。另请参阅this thread上的ompf

(引用佩林)

# man syscalls
# cat phat.cc
extern "C" void _start() {
        asm("int $0x80" :: "a"(1), "b"(42));
}
# g++ -fno-exceptions -Os -c phat.cc
# objdump -d phat.o

phat.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <_start>:
   0:   53                      push   %rbx
   1:   b8 01 00 00 00          mov    $0x1,%eax
   6:   bb 2a 00 00 00          mov    $0x2a,%ebx
   b:   cd 80                   int    $0x80
   d:   5b                      pop    %rbx
   e:   c3                      retq
# ld -nostdlib -nostartfiles phat.o -o phat
# sstrip phat
# ls -l phat
-rwxr-xr-x 1 tbp src 294 2007-04-11 22:47 phat
# ./phat; echo $?
42

总结:上面的代码片段产生了一个 294字节的二进制文件,每个字节为8位。

答案 2 :(得分:5)

假设还允许使用其他工具; - )

然后考虑使用运行时解压缩的UPX: the Ultimate Packer for Binaries

快乐的编码。

答案 3 :(得分:5)

如果你想从二进制文件中挤出最后一滴空间,你可能需要学习装配。对于一个非常有趣(和有趣)的介绍,请看这个链接:

A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux

答案 4 :(得分:4)

它还取决于您使用的架构。

在手臂上,你有Thumb指令集来减少生成的代码大小。

您还可以避免动态链接,并且更喜欢仅由您的程序使用的库或您系统上的极少数程序的静态链接。这不会减小生成的二进制文件本身的大小,但总体而言,您将在系统上使用较少的空间来执行此程序。

答案 5 :(得分:2)

使用 strip (1)时,您需要确保使用所有相关选项。出于某种原因,--strip-all并不总是剥离一切。删除不必要的部分可能会有所帮助。

但最终,减小二进制文件大小的最佳方法是从程序中删除代码和静态数据。让它做得更少,或选择导致更少指令的编程结构。例如,您可以在运行时构建数据结构,或者按需从文件加载它们,而不是使用静态初始化的数组。

答案 6 :(得分:2)

您可以尝试使用-fdata-sections-ffunction-sections-Wl,--gc-sections,但这不安全,因此请务必在使用它们之前了解它们的工作原理。

答案 7 :(得分:1)

以下代码大小优化标志可用于减少代码大小:

  • -Os(gcc 和 llvm),-Oz(仅 LLVM)
  • -fno-unroll-loops
  • -fno-exceptions/-fno-rtti
  • -finline-limit=n (gcc), -mllvm -inline-threshold=n (llvm)
  • -flto, -flto=thin(仅限 LLVM)
  • -fmerge-functions(仅限 LLVM)。不过,此优化可能存在一些错误。

您可能还想查看最近在嵌入式 Linux 大会上的演示文稿。它提供了一套全面的编译器优化和其他软件工程技术,以减少代码大小。

Link to presentation

slide deck

免责声明:我是主持人。

答案 8 :(得分:0)

我只是想补充一下Chris Eberle's answer,如果你想学习ARM汇编,here你也可以学习如何调用C库函数和使用ARM汇编中的动态库;而且,如果你需要一个 ARM 虚拟机,你可以去here.