Как уменьшить размер сгенерированных двоичных файлов? - PullRequest
13 голосов
/ 21 июля 2011

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

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

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

Ответы [ 7 ]

13 голосов
/ 21 июля 2011

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

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

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

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

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

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

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

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

7 голосов
/ 21 июля 2011

Вы также можете использовать -nostartfiles и / или -nodefaultlibs или комбинацию обоих -nostdlib. Если вам не нужен стандартный стартовый файл, вы должны написать свою собственную функцию _start. См. Также эту тему в 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 бит.

5 голосов
/ 21 июля 2011

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

Учебное пособие по созданию действительно исполняемых файлов ELF для Linux

5 голосов
/ 21 июля 2011

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

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

Удачного кодирования.

4 голосов
/ 21 июля 2011

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

На руке у вас есть набор команд Thumb, который здесь для уменьшения размера сгенерированного кода.

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

2 голосов
/ 02 ноября 2011

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

2 голосов
/ 21 июля 2011

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

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

...