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

发布于 2024-11-25 12:04:07 字数 122 浏览 7 评论 0原文

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

strip(或“-s”选项)删除调试符号表,这可以工作很好;但只能减少一小部分大小,

还有其他方法吗?

I know that there is an option "-Os" to "Optimize for size", but it has little affect, or even increase the size on some occasion :(

strip (or "-s" option) removes debug symbol table, which works fine; but it can only decrease only a small propotion of the size.

Is there any other way to go furthur?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

旧梦荧光笔 2024-12-02 12:04:07

除了明显的(-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.cbaz.c 首先到目标文件,然后链接)将具有类似的效果。它使优化器一次性看到所有内容,从而可能使其更好地工作。

-fdelete-null-pointer-checks 可能是一个选项(请注意,这通常通过任何“O”启用,但在嵌入式目标上)。

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

避免像魔鬼一样使用非平凡构造函数静态本地变量(POD类型没有问题)。 Gcc 将初始化非平凡构造函数静态局部线程安全,除非您使用 -fno-threadsafe-statics 进行编译,它会链接很多额外代码(即使您不这样做)根本不使用线程)。

使用 libowfat 之类的东西代替普通的 crt 可以大大减少二进制文件的大小。

Apart from the obvious (-Os -s), aligning functions to the smallest possible value that will not crash (I don't know ARM alignment requirements) might squeeze out a few bytes per function.
-Os should already disable aligning functions, but this might still default to a value like 4 or 8. If aligning e.g. to 1 is possible with ARM, that might save some bytes.

-ffast-math (or the less abrasive -fno-math-errno) will not set errno and avoid some checks, which reduces code size. If, like most people, you don't read errno anyway, that's an option.

Properly using __restrict (or restrict) and const removes redundant loads, making code both faster and smaller (and more correct). Properly marking pure functions as such eleminates function calls.

Enabling LTO may help, and if that is not available, compiling all source files into a binary in one go (gcc foo.c bar.c baz.c -o program instead of compiling foo.c, bar.c, and baz.c to object files first and then linking) will have a similar effect. It makes everything visible to the optimizer at one time, possibly allowing it to work better.

-fdelete-null-pointer-checks may be an option (note that this is normally enabled with any "O", but not on embedded targets).

Putting static globals (you hopefully don't have that many, but still) into a struct can eleminate a lot of overhead initializing them. I learned that when writing my first OpenGL loader. Having all the function pointers in a struct and initializing the struct with = {} generates one call to memset, whereas initializing the pointers the "normal way" generates a hundred kilobytes of code just to set each one to zero individually.

Avoid non-trivial-constructor static local variables like the devil (POD types are no problem). Gcc will initialize non-trivial-constructor static locals threadsafe unless you compile with -fno-threadsafe-statics, which links in a lot of extra code (even if you don't use threads at all).

Using something like libowfat instead of the normal crt can greatly reduce your binary size.

提笔落墨 2024-12-02 12:04:07

您还可以使用 - nostartfiles 和/或 -nodefaultlibs 或两者的组合 -nostdlib。如果您不需要标准启动文件,则必须编写自己的 _start 函数。另请参阅此帖子(已存档) on ompf:(

引用 Perrin)

# 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 位。

You can also use -nostartfiles and/or -nodefaultlibs or the combo of both -nostdlib. In case you don't want a standard start file, you must write your own _start function then. See also this thread (archived) on oompf:

(quoting Perrin)

# 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

Summary: Above snippet yielded a binary of 294 bytes, each byte 8 bits.

初见 2024-12-02 12:04:07

如果您想从二进制文件中挤出最后一滴空间,您可能必须学习汇编。有关非常有趣(且有趣)的介绍,请参阅此链接:

旋风教程为 Linux 创建真正很小的 ELF 可执行文件

If you want to squeeze every last drop of space out of your binaries, you'll probably have to learn assembly. For a very interesting (and entertaining) intro, see this link:

A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux

我很OK 2024-12-02 12:04:07

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

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

When using strip(1), you'll want to make sure you use all the relevant options. For some reason, --strip-all doesn't always strip everything. Removing unnecessary sections may be helpful.

Ultimately, though, the best way to reduce the size of the binary is to remove code and static data from the program. Make it do less, or select programming constructs that result in fewer instructions. For example, you might build data structures at runtime, or load them from a file, on-demand, rather than have a statically initialized array.

污味仙女 2024-12-02 12:04:07

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

  • -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 会议上的最新演示。它提供了一套全面的编译器优化以及其他软件工程技术来减少代码大小。

链接到演示文稿

幻灯片

免责声明:我是演示者。

Following code size optimization flags can be used to reduce code size:

  • -Os (both gcc and llvm), -Oz (only LLVM)
  • -fno-unroll-loops
  • -fno-exceptions/-fno-rtti
  • -finline-limit=n (gcc), -mllvm -inline-threshold=n (llvm)
  • -flto, -flto=thin (only LLVM)
  • -fmerge-functions (only LLVM). There might be some bugs in this optimization though.

You might also want to checkout a recent presentation at the Embedded Linux Conference. It presents a comprehensive set of compiler optimizations, and other software engineering techniques to reduce code size.

Link to presentation

slide deck

Disclaimer: I'm the presenter.

浅黛梨妆こ 2024-12-02 12:04:07

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

在 Arm 上,您拥有 Thumb 指令集,用于减少生成的代码大小。

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

It also depends on the architecture you are using.

On arm, you have the Thumb instruction set that is here to reduce the generated code size.

You can also avoid dynamic linking and prefer static linking for libs only used by your program or very few programs on your system. This will not decrease the size of your generated binary per se, but overall, you will use less space on your system for this program.

油焖大侠 2024-12-02 12:04:07

您可以尝试使用 -fdata-sections-ffunction-sections-Wl,--gc-sections,但这并不安全,所以在使用它们之前一定要了解它们的工作原理。

You can try playing with -fdata-sections, -ffunction-sections and -Wl,--gc-sections, but this is not safe, so be sure to understand how they work before using them.

别再吹冷风 2024-12-02 12:04:07

我只是想补充一下 Chris Eberle 的回答,如果你想学习 ARM 汇编,在这里您还可以学习如何调用C库函数以及使用ARM汇编的动态库;而且,如果您需要 ARM 虚拟机,可以访问此处

I just wanted to add to Chris Eberle's answer that, if you want to learn ARM assembly, here you can also learn how to call C library functions and use dynamic libraries from ARM assembly; and, if you need an ARM virtual machine, you can go here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文