вопрос о блоке i386: почему я должен влезть в указатель вершины стека?

Я решил, что это будет забава изучить 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, был бы он когда-либо иметь значение.)

6
задан starblue 6 June 2010 в 20:08
поделиться

3 ответа

В 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-битной сборки.

3
ответ дан 17 December 2019 в 00:03
поделиться

Общая форма комментария должна быть такой: "Выделяет место для локальных переменных". Почему произвольное изменение этого параметра приведет к краху, я не знаю. Я вижу, что он может упасть, только если вы его уменьшите. А правильный комментарий для 6 - "Приготовьтесь вернуть 0 из этой функции".

3
ответ дан 17 December 2019 в 00:03
поделиться

Обратите внимание, что при компиляции с -fomit-frame-pointer некоторые из этих %ebp указателей исчезнут. Базовый указатель полезен для отладки, но на самом деле не нужен на x86.

Также я настоятельно рекомендую использовать синтаксис Intel, который поддерживается всеми GCC/binutils. Раньше я думал, что разница между синтаксисом AT&T и Intel - это просто дело вкуса, но однажды я наткнулся на этот пример, где мнемоника AT&T просто полностью отличается от мнемоники Intel. А поскольку вся официальная документация по x86 использует синтаксис Intel, это кажется более правильным решением.

Веселитесь!

1
ответ дан 17 December 2019 в 00:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: