Ошибка проверки времени выполнения # 0 во встроенном коде asm - PullRequest
1 голос
/ 05 мая 2011

Я немного новичок в ассемблере, но я пытаюсь найти параметры из метода C ++ в стеке esp, используя встроенный код ассемблера. До сих пор мне даже не удалось скопировать указатель esp в ebp, чтобы я мог удержать стек (на случай, если он изменится). Даже этот маленький кусочек кода дает мне ошибку:

#include <stdlib.h>

int main(int argc, char* argv[])
{
    __asm
    {
        mov ebp, esp
    }
    system("pause");
    return 0;
}

После запуска я получаю:

Ошибка проверки времени выполнения # 0 - значение ESP не было должным образом сохранено при вызове функции. Обычно это является результатом вызова функции, объявленной с одним соглашением о вызовах с указателем функции, объявленным с другим соглашением о вызовах.

Не знаю, что делать. Помогите мне, пожалуйста, и заранее спасибо.

1 Ответ

2 голосов
/ 05 мая 2011

Как правило, вы должны поместить текущее значение EBP в стек, прежде чем перемещать значение ESP в EBP. EBP - это регистр «сохранения-вызова» на 32-битных платформах. Это означает, что если вы собираетесь изменить его в функции, вы должны сначала сохранить его.

Если вы хотите, чтобы ваша функция возвращала значение того, куда указывает стек (или куда она вернется после вызова функции), лучше всего сделать следующее:

void* get_stack_addr()
{
        void* stack_ptr = NULL;

        //on 32-bit systems
        //EBP is pointing at top of stack frame
        //EBP + 4 is the return instruction address
        //EBP + 8 is the value that was in ESP before function call for a function with no arguments
        __asm__
        (
                "movl %%ebp, %0\n\t"
                "addl $8, %0\n\t"
                : "=r" (stack_ptr)
        );

        return stack_ptr;
}

Таким образом, EAX теперь содержит адрес значения, на которое указывал стек до вызова get_stack_addr(). Если вы только что вернули значение ESP в функции, вы на самом деле понятия не имеете, на что указываете, поскольку компилятор часто дополняет стек в функции C / C ++, чтобы поддерживать правильное выравнивание стека. Он также часто резервирует место в стеке для всех локальных переменных, что опять-таки скинет вычисление стека. Используя EBP, который указывает на вершину стекового фрейма, вы можете точно рассчитать на 32-битной платформе значение стека до вызова функции. Наконец, мы помещаем возвращаемое значение в EAX, поскольку в большинстве двоичных интерфейсов приложений ОС для C / C ++ EAX содержит возвращаемое значение функции, а не EBP.

Еще одна вещь ... если вы хотите запустить параметры в стеке для фактической функции, которая вызывает get_stack_addr(), то измените movl %%ebp, %0\n\t на movl (%%ebp), %0)\n\t. Таким образом, теперь вы получаете предыдущий базовый указатель стекового кадра (т. Е. Базовый указатель стекового кадра вызывающего абонента), и, добавив значение +8 к этому адресу, вы получаете либо начало сохраненных параметров выше адреса возврата, или вы смотрите адрес, указывающий на кадр стека для вызывающей стороны текущей функции (т. е. предыдущего кадра стека).


В качестве улучшения "leal 8(%%ebp), %0\n\t" можно заменить:

"movl %%ebp, %0\n\t"
"addl $8, %0\n\t"

Эта инструкция leal добавит 8 к значению EBP и сохранит результат в выходном операнде.

...