Почему функция GCC работает с NOP?

Я работал с C на короткое время и совсем недавно начал заниматься ASM. Когда я скомпилирую программу:

int main(void) { int a = 0; a += 1; return 0; } 

Разборка objdump имеет код, но nops после ret:

 ... 08048394 : 8048394: 55 push %ebp 8048395: 89 e5 mov %esp,%ebp 8048397: 83 ec 10 sub $0x10,%esp 804839a: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp) 80483a1: 83 45 fc 01 addl $0x1,-0x4(%ebp) 80483a5: b8 00 00 00 00 mov $0x0,%eax 80483aa: c9 leave 80483ab: c3 ret 80483ac: 90 nop 80483ad: 90 nop 80483ae: 90 nop 80483af: 90 nop ... 

Из того, что я узнал, ничего не делают, и с тех пор, как ret не будет даже выполнен.

Мой вопрос: зачем беспокоиться? Не может ли ELF (linux-x86) работать с секцией .text (+ main) любого размера?

Я был бы признателен за любую помощь, просто пытаясь научиться.

Прежде всего, gcc не всегда это делает. -falign-functions контролируется -falign-functions , которые автоматически -falign-functions -O2 и -O3 :

-falign-functions
-falign-functions=n

Выровняйте начало функций до следующего значения мощности в два больше n , пропуская до n байтов. Например, -falign-functions=32 выравнивает функции к следующей 32-байтовой границе, но -falign-functions=24 будет выравниваться с следующей 32-байтовой границей, только если это можно сделать, пропустив 23 байта или меньше.

-fno-align-functions и -falign-functions=1 эквивалентны и означают, что функции не будут выровнены.

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

Если n не указано или равно нулю, используйте зависящее от машины значение по умолчанию.

Включено на уровнях -O2, -O3.

Для этого может быть несколько причин, но главная из них на x86, вероятно, такова:

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

(Цитата из раздела «Оптимизация подпрограмм на языке ассемблера» Агнера Фога).

edit: Вот пример, демонстрирующий дополнение:

 // align.c int f(void) { return 0; } int g(void) { return 0; } 

При компиляции с использованием gcc 4.4.5 с настройками по умолчанию я получаю:

 align.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 : 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: b8 00 00 00 00 mov $0x0,%eax 9: c9 leaveq a: c3 retq 000000000000000b : b: 55 push %rbp c: 48 89 e5 mov %rsp,%rbp f: b8 00 00 00 00 mov $0x0,%eax 14: c9 leaveq 15: c3 retq 

Указание -falign-functions дает:

 align.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 : 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: b8 00 00 00 00 mov $0x0,%eax 9: c9 leaveq a: c3 retq b: eb 03 jmp 10  d: 90 nop e: 90 nop f: 90 nop 0000000000000010 : 10: 55 push %rbp 11: 48 89 e5 mov %rsp,%rbp 14: b8 00 00 00 00 mov $0x0,%eax 19: c9 leaveq 1a: c3 retq 

Это делается для выравнивания следующей функции с помощью границы 8, 16 или 32 байта.

Из раздела «Оптимизация подпрограмм на языке ассемблера» A.Fog:

11.5 Выравнивание кода

Большинство микропроцессоров извлекают код в выровненных 16-байтных или 32-байтовых блоках. Если запись importantubroutine или метка перехода находится ближе к концу 16-байтового блока, тогда на микропроцессор будет извлекаться только несколько полезных байтов кода при извлечении этого блока кода. Itmay должен получить следующие 16 байт, прежде чем он сможет декодировать первые инструкции после метки. Этого можно избежать, совместив важные записи подпрограмм и записи циклов на 16.

[…]

Выравнивание записи подпрограммы так же просто, как и любое количество NOP, если необходимо, до ввода функции, чтобы сделать адрес делящимся на 8, 16, 32 или 64, по желанию.

Насколько я помню, инструкции конвейерны в процессоре, а разные блоки процессора (загрузчик, декодер и т. Д.) Обрабатывают последующие инструкции. Когда выполняются инструкции RET , несколько инструкций уже загружаются в конвейер cpu. Это предположение, но вы можете начать копать здесь, и если вы узнаете (возможно, определенное количество NOP , которые безопасны, поделитесь своими результатами, пожалуйста.

  • Можно ли получить gcc для чтения из трубы?
  • Операция сравнения для целых чисел без знака и знака
  • Как удалить неиспользуемые символы C / C ++ с помощью GCC и ld?
  • Для чего __gxx_personality_v0?
  • Попытка понять параметр gcc -fomit-frame-pointer
  • Почему gcc разрешает передавать аргументы в функцию, определенную без аргументов?
  • сценарий установки вышел с ошибкой: команда «x86_64-linux-gnu-gcc» не удалась с статусом выхода 1
  • Разница в производительности между MSVC и GCC для высоко оптимизированного матричного кода
  • Почему порядок, в котором связаны библиотеки, иногда вызывает ошибки в GCC?
  • Вложение бинарных капель с использованием gcc mingw
  • выровненный malloc () в GCC?
  • Давайте будем гением компьютера.