Вопрос сборки i386: зачем мне вмешиваться с указателем стека? - PullRequest
6 голосов
/ 06 июня 2010

Я решил, что будет забавно изучить сборку x86 во время летних каникул. Поэтому я начал с очень простой программы hello world, позаимствовав на бесплатных примерах 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, это когда-нибудь будет иметь значение.)

Ответы [ 3 ]

3 голосов
/ 06 июня 2010

В x86 OSX стек должен быть выровнен на 16 байт для вызовов функций, см. ABI doc здесь . Итак, объяснение

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 голосов
/ 06 июня 2010

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

1 голос
/ 06 июня 2010

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

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

Веселись!

...