Как уменьшить размер сгенерированных двоичных файлов?

Я знаю, что есть опция "- Os " для "оптимизации по размеру", но она мало влияет или даже увеличивает размер в некоторых случаях :(

strip (или опция"- s") удаляет таблицу символов отладки, которая отлично работает; но она может уменьшить только небольшое предложение размера.

есть ли другой способ пойти дальше?

7 ответов


помимо очевидных (-Os -s), выравнивание функций до наименьшего возможного значения, которое не приведет к сбою (я не знаю требований к выравниванию ARM), может выдавить несколько байтов на функцию.
-Os должны уже отключены функции выравнивания, но по умолчанию это может быть значение 4 или 8. Если выравнивание, например, до 1 возможно с ARM, это может сэкономить несколько байтов.

-ffast-math (или менее абразивный -fno-math-errno) не будет устанавливать errno и избегать некоторых проверяет, что уменьшает размер кода. Если, как и большинство людей, вы все равно не читаете errno, это вариант.

правильно используя __restrict (или restrict) и const удаляет избыточные нагрузки, делая код быстрее и меньше (и более правильным). Правильная маркировка чистых функций как таких вызовов функций eleminates.

включение LTO может помочь, и если это недоступно, компиляция всех исходных файлов в двоичный файл за один раз (gcc foo.c bar.c baz.c -o program вместо компиляции foo.c, bar.c, и baz.c сначала объектные файлы, а затем связывание) будет иметь аналогичный эффект. Это делает все видимым для оптимизатора в одно время, возможно, позволяя ему работать лучше.

-fdelete-null-pointer-checks может быть опция (обратите внимание, что это обычно включено с любым "O", но не по встроенным целям).

положить статические глобалы (вы, надеюсь, не так много, но все же) в структуру может eleminate много накладных инициализация их. Я узнал это при написании моего первого загрузчика OpenGL. Наличие всех указателей функций в структуре и инициализация структуры с помощью = {} генерирует один вызов memset, в то время как инициализация указателей "обычным способом" генерирует сто килобайт кода, чтобы установить каждый из них в ноль отдельно.

избегайте нетривиального конструктора static местные переменные, такие как дьявол (типы POD не являются проблемой). Gcc инициализирует нетривиальные статические локальные конструкторы threadsafe, если вы не компилируете с -fno-threadsafe-statics, что ссылки в большое дополнительного кода (даже если вы вообще не используете потоки).

Используя что-то вроде libowfat вместо обычного crt can значительно уменьшить бинарный размер.


вы также можете использовать -nostartfiles и/или -nodefaultlibs или комбинация обоих -nostdlib. Если вам не нужен стандартный файл запуска, вы должны написать свою собственную функцию _start. См. также этой теме on проникновения в клетку:

(цитирую Перрен)

# man syscalls
# cat phat.cc
extern "C" void _start() {
        asm("int x80" :: "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    x1,%eax
   6:   bb 2a 00 00 00          mov    x2a,%ebx
   b:   cd 80                   int    x80
   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: выше фрагмент дал двоичный файл 294 байт, каждый байт 8 бит.


предполагая, что другой инструмент также разрешен ; -)

затем рассмотреть UPX: окончательный упаковщик для двоичных файлов который использует декомпрессию во время выполнения.

удачи в кодировании.


Если вы хотите выжать каждую последнюю каплю пространства из своих двоичных файлов, вам, вероятно, придется изучить сборку. Для очень интересного (и занимательного) вступления см. Эту ссылку:

вихрь учебник по созданию действительно Teensy Elf исполняемые файлы для Linux


Это также зависит от используемой вами архитектуры.

на arm у вас есть набор инструкций Thumb, который здесь, чтобы уменьшить размер сгенерированного кода.

вы также можете избежать динамической компоновки и предпочитают статическую компоновку для библиотек, используемых только вашей программой или очень мало программ в вашей системе. Это не уменьшит размер сгенерированного двоичного файла как такового, но в целом вы будете использовать меньше места в своей системе для этой программы.


при использовании прокладки(1), вы хотите убедиться, что используете все соответствующие параметры. По какой причине --strip-all не всегда все полосы. Удаление ненужных разделов может быть полезно.

в конечном счете, лучший способ уменьшить размер исполняемого файла, чтобы удалить код и статические данные из программы. Сделайте это меньше или выберите конструкции программирования, которые приводят к меньшему количеству инструкций. Например, можно создать структуры данных во время выполнения или загрузить их из файла, по требованию, а не статически инициализируется массив.


вы можете попробовать сыграть с -fdata-sections, -ffunction-sections и -Wl,--gc-sections, но это небезопасно, поэтому обязательно поймите, как они работают, прежде чем использовать их.