Я работал с C некоторое время и совсем недавно начал разбираться в ASM. Когда я компилирую программу:
int main(void)
{
int a = 0;
a += 1;
return 0;
}
В дизассемблере objdump есть код, но после ret нет:
...
08048394 <main>:
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
...
Из того, что я узнал, nops ничего не делает, и поскольку после ret даже не будет выполняться.
У меня вопрос: зачем беспокоиться? Не может ELF (linux-x86) работать с разделом .text (+ main) любого размера?
Буду признателен за любую помощь, просто пытаюсь учиться.
80483af
, возможно, это заполнение, чтобы выровнять следующую функцию до 8 или 16 байтов.Ответы:
Во-первых, так
gcc
бывает не всегда. Заполнение контролируется-falign-functions
, которое автоматически включается-O2
и-O3
:Для этого может быть несколько причин, но основная из них на x86, вероятно, следующая:
(Цитата из «Оптимизация подпрограмм на ассемблере» Агнера Фога.)
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 $0x0,%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 $0x0,%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 $0x0,%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 $0x0,%eax 19: c9 leaveq 1a: c3 retq
источник
-falign-functions
.main
в исполняемом файле (в моем случае это функция__libc_csu_fini
).Это делается для выравнивания следующей функции по границе 8, 16 или 32 байта.
Из «Оптимизация подпрограмм на ассемблере» А.Фога:
источник
foo
0x1234, то код, который использует этот адрес в непосредственной близости от литерала 0x1234, может в конечном итоге создать машинный код,mov ax,0x1234 / push ax / mov ax,0x1234 / push ax
который оптимизатор затем может заменить наmov ax,0x1234 / push ax / push ax
. Обратите внимание, что функции не должны перемещаться после такой оптимизации, поэтому удаление инструкций улучшит скорость выполнения, но не размер кода.Насколько я помню, инструкции передаются по конвейеру в процессоре, а различные блоки процессора (загрузчик, декодер и т. Д.) Обрабатывают последующие инструкции. Когда
RET
инструкции выполняются, несколько следующих инструкций уже загружены в конвейер процессора. Это предположение, но вы можете начать копать здесь и, если узнаете (может быть, конкретное количествоNOP
безопасных s, поделитесь своими выводами, пожалуйста.источник
nop
s).ret
.ud2
или,int3
которые всегда дают сбой, поэтому интерфейсная часть знает, что вместо этого нужно остановить декодирование например, подачи потенциально дорогостоящейdiv
или ложной загрузки TLB-промаха в конвейер. Это не нужно послеret
или прямогоjmp
хвостового вызова в конце функции.