Я должен предварять все свои комментарии, говоря, что я все еще учусь усердно.
Я буду игнорировать инициализацию раздела. Объяснение инициализации раздела и, в основном, всего остального, что я рассмотрю, можно найти здесь:
http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax
Регистр ebp - это стековый фрейм базовый указатель, следовательно, BP. Он хранит указатель на начало текущего стека.
Регистр esp является указателем стека. Он хранит память в верхней части стека. Каждый раз, когда мы помещаем что-либо в стек, esp обновляется, так что он всегда указывает на адрес, находящийся на вершине стека.
Таким образом, ebp указывает на базу, а esp указывает на вершину. Так что стек выглядит так:
esp -----> 000a3 fa
000a4 21
000a5 66
000a6 23
esb -----> 000a7 54
Если вы кладете e4 в стек, вот что происходит:
esp -----> 000a2 e4
000a3 fa
000a4 21
000a5 66
000a6 23
esb -----> 000a7 54
Обратите внимание, что стек увеличивается по направлению к младшим адресам, этот факт будет важен ниже.
Первые два шага известны как пролог процедуры или, более часто, пролог функции , они подготавливают стек для использования локальными переменными. Смотри цитату пролога процедуры внизу.
На шаге 1 мы сохраняем указатель на старый кадр стека в стеке, вызывая
pushl% ebp. Поскольку main - это первая вызванная функция, я не знаю, какое предыдущее значение указывает% ebp.
Шаг 2, Мы вводим новый кадр стека, потому что мы вводим новую функцию (основную). Следовательно, мы должны установить новый базовый указатель фрейма стека. Мы используем значение в esp, чтобы быть началом нашего стекового фрейма.
Шаг 3. Выделяет 8 байт пространства в стеке. Как мы уже упоминали выше, стек увеличивается к младшим адресам, таким образом, вычитая на 8, перемещает вершину стека на 8 байтов.
Шаг 4; Выравнивая стек, я нашел разные мнения по этому поводу. Я не совсем уверен, что именно это делается. Я подозреваю, что это сделано для того, чтобы в стеке могли размещаться большие инструкции (SIMD),
http://gcc.gnu.org/ml/gcc/2008-01/msg00282.html
Этот код "а" ESP с 0xFFFF0000,
выравнивание стека со следующим
младшая 16-байтовая граница.
проверка исходного кода Mingw
показывает, что это может быть для SIMD
инструкции, появляющиеся в "_main"
рутины, которые действуют только по согласованию
адреса. Поскольку наша рутина не
содержат инструкции SIMD, эта строка
не нужен.
http://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax
Шаги с 5 по 11 кажутся мне бесполезными. Я не мог найти никакого объяснения в Google. Может ли тот, кто действительно знает этот материал, дать более глубокое понимание. До меня дошли слухи, что этот материал используется для обработки исключений в Си.
Шаг 5, сохраняет возвращаемое значение main 0 в eax.
Шаг 6 и 7 мы добавляем 15 в гекс к eax по неизвестной причине. eax = 01111 + 01111 = 11110
Шаг 8 мы сдвигаем биты eax 4 бита вправо. eax = 00001, потому что последние биты сдвинуты от конца 00001 | 111.
Шаг 9 мы сдвигаем биты 4-х бит eax влево, eax = 10000.
Шаги 10 и 11 перемещают значение в первых 4 выделенных байтах стека в eax, а затем перемещают его из eax обратно.
Шаги 12 и 13 настраивают библиотеку c.
Мы достигли функционального эпилога . То есть часть функции, которая возвращает указатели стека esp и ebp в состояние, в котором они находились до вызова этой функции.
Шаг 14, оставьте устанавливает esp значение ebp, перемещая вершину стека по адресу, который был до вызова main. Затем он устанавливает ebp, чтобы он указывал на адрес, который мы сохранили на вершине стека во время шага 1.
Отпуск можно заменить следующими инструкциями:
mov %ebp, %esp
pop %ebp
Шаг 15, возврат и выход из функции.
1. pushl %ebp
2. movl %esp, %ebp
3. subl $8, %esp
4. andl $-16, %esp
5. movl $0, %eax
6. addl $15, %eax
7. addl $15, %eax
8. shrl $4, %eax
9. sall $4, %eax
10. movl %eax, -4(%ebp)
11. movl -4(%ebp), %eax
12. call __alloca
13. call ___main
14. leave
15. ret
Процедура Пролог:
Первое, что должна сделать функция
называется процедурой пролог. Это
сначала сохраняет текущий базовый указатель
(ebp) с инструкцией pushl% ebp
(помните, ebp - это регистр, используемый длядоступ к параметрам функции и
локальные переменные). Теперь он копирует
указатель стека (esp) на базу
указатель (ebp) с инструкцией
movl% esp,% ebp. Это позволяет вам
получить доступ к параметрам функции как
индексы из базового указателя. Местный
переменные всегда вычитание
от ebp, например -4 (% ebp) или
(% ebp) -4 для первой локальной переменной,
возвращаемое значение всегда в 4 (% ebp)
или (% ebp) +4, каждый параметр или
аргумент в N * 4 + 4 (% ebp), такой как
8 (% ebp) для первого аргумента в то время как
старый ebp равен (% ebp).
http://www.milw0rm.com/papers/52
Существует действительно отличный поток переполнения стека, который отвечает на большую часть этого вопроса.
Почему в выводе gcc есть дополнительные инструкции?
Хорошую ссылку на инструкции машинного кода x86 можно найти здесь:
http://programminggroundup.blogspot.com/2007/01/appendix-b-common-x86-instructions.html
Это лекция, которая содержит некоторые из идей, использованных ниже:
http://csc.colstate.edu/bosworth/cpsc5155/Y2006_TheFall/MySlides/CPSC5155_L23.htm
Вот еще один ответ на ваш вопрос:
http://www.phiral.net/linuxasmone.htm
Ни один из этих источников не объясняет все.