Как уменьшить размер сгенерированных двоичных файлов?
Я знаю, что есть опция "- 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
, но это небезопасно, поэтому обязательно поймите, как они работают, прежде чем использовать их.