Почему GCC pad работает с NOPs?
Я работал с C в течение короткого времени и совсем недавно начал входить в ASM. Когда я компилирую программу:
int main(void)
{
int a = 0;
a += 1;
return 0;
}
разборка objdump имеет код, но nops после ret:
...
08048394 <main>:
8048394: 55 push %ebp
8048395: 89 e5 mov %esp,%ebp
8048397: 83 ec 10 sub x10,%esp
804839a: c7 45 fc 00 00 00 00 movl x0,-0x4(%ebp)
80483a1: 83 45 fc 01 addl x1,-0x4(%ebp)
80483a5: b8 00 00 00 00 mov x0,%eax
80483aa: c9 leave
80483ab: c3 ret
80483ac: 90 nop
80483ad: 90 nop
80483ae: 90 nop
80483af: 90 nop
...
из того, что я узнал, nops ничего не делают, и так как после ret даже не будет выполнен.
мой вопрос: зачем? Не удалось ELF (linux-x86) работать с a .текстовый раздел (+main) любого размера?
Я был бы признателен за любую помощь, просто пытаясь учить.
3 ответа:
прежде всего,
gcc
не всегда это делает. Заполнение контролируется-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 <f>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: b8 00 00 00 00 mov x0,%eax 9: c9 leaveq a: c3 retq 000000000000000b <g>: b: 55 push %rbp c: 48 89 e5 mov %rsp,%rbp f: b8 00 00 00 00 mov x0,%eax 14: c9 leaveq 15: c3 retq
задание
-falign-functions
выдает:align.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <f>: 0: 55 push %rbp 1: 48 89 e5 mov %rsp,%rbp 4: b8 00 00 00 00 mov x0,%eax 9: c9 leaveq a: c3 retq b: eb 03 jmp 10 <g> d: 90 nop e: 90 nop f: 90 nop 0000000000000010 <g>: 10: 55 push %rbp 11: 48 89 e5 mov %rsp,%rbp 14: b8 00 00 00 00 mov x0,%eax 19: c9 leaveq 1a: c3 retq
Это делается для выравнивания следующей функции по 8, 16 или 32-байтовой границе.
из "оптимизация подпрограмм на языке ассемблера" А. фог:
11.5 выравнивание кода
большинство микропроцессоров получают код в выровненных 16-байтовых или 32-байтовых блоках. Если запись importantsubroutine или метка перехода оказывается ближе к концу 16-байтового блока, то themicroprocessor получит только несколько полезных байтов кода при извлечении этого блока кода. Он может нужно также извлечь следующие 16 байт, прежде чем он сможет декодировать первые инструкции после метки. Этого можно избежать, выровняв важные элементы подпрограммы и элементы цикла на 16.
[...]
выравнивание записи подпрограммы так же просто, как положить столько НОП 's по мере необходимости перед записью thesubroutine, чтобы сделать адрес делится на 8, 16, 32 или 64, по желанию.
насколько я помню, инструкции передаются по конвейеру в cpu и различные блоки cpu (загрузчик, декодер и т. д.) обрабатывают последующие инструкции. Когда
RET
инструкции выполняется несколько инструкций уже загружены в конвейер процессора. Это предположение, но вы можете начать копать здесь, и если вы узнаете (возможно, определенное количествоNOP
s, которые безопасны, поделитесь своими выводами, пожалуйста.