Я решил, что это будет забава изучить x86 блок во время летних каникул. Таким образом, я запустил с очень простым привет мировую программу, одалживающую на бесплатных примерах gcc -S
мог дать мне. Я закончил с этим:
HELLO:
.ascii "Hello, world!\12\0"
.text
.globl _main
_main:
pushl %ebp # 1. puts the base stack address on the stack
movl %esp, %ebp # 2. puts the base stack address in the stack address register
subl $20, %esp # 3. ???
pushl $HELLO # 4. push HELLO's address on the stack
call _puts # 5. call puts
xorl %eax, %eax # 6. zero %eax, probably not necessary since we didn't do anything with it
leave # 7. clean up
ret # 8. return
# PROFIT!
Это компилирует и даже работает! И я думаю, что понимаю большую часть из него.
Хотя, волшебство происходит на шаге 3. Я удалил бы эту строку, моя программа умрет между вызовом к puts
и xor
от неправильно выровненной ошибки стека. И я изменился бы $20
к другому значению это отказало бы также. Таким образом, я пришел к выводу, что это значение very
важный.
Проблема, я не знаю то, что она делает и почему она необходима.
Кто-либо может объяснить меня? (Я нахожусь на Mac OS, был бы он когда-либо иметь значение.)
В x86 OSX стек должен быть выровнен по 16 байтам для вызовов функций, см. Документ ABI здесь . Итак, объяснение таково:
push stack pointer (#1) -4 strange increment (#3) -20 push argument (#4) -4 call pushes return address (#5) -4 total -32
Чтобы проверить, измените строку №3 с 20 долларов на 4 доллара, что также работает.
Кроме того, Игнасио Васкес-Абрамс указывает, что №6 не является обязательным. Регистры содержат остатки предыдущих вычислений, поэтому их необходимо явно обнулить.
Я недавно выучил (еще изучаю) ассемблер. Чтобы спасти вас от шока, 64-битные соглашения о вызовах НАМНОГО отличаются (параметры передаются в регистре). Нашел этот очень полезным для 64-битной сборки.
Общая форма комментария должна быть такой: "Выделяет место для локальных переменных". Почему произвольное изменение этого параметра приведет к краху, я не знаю. Я вижу, что он может упасть, только если вы его уменьшите. А правильный комментарий для 6 - "Приготовьтесь вернуть 0 из этой функции".
Обратите внимание, что при компиляции с -fomit-frame-pointer некоторые из этих %ebp
указателей исчезнут. Базовый указатель полезен для отладки, но на самом деле не нужен на x86.
Также я настоятельно рекомендую использовать синтаксис Intel, который поддерживается всеми GCC/binutils. Раньше я думал, что разница между синтаксисом AT&T и Intel - это просто дело вкуса, но однажды я наткнулся на этот пример, где мнемоника AT&T просто полностью отличается от мнемоники Intel. А поскольку вся официальная документация по x86 использует синтаксис Intel, это кажется более правильным решением.
Веселитесь!