При разборе одного рабочего проекта столкнулся со странной проблемой: отладочная версия «падала» с ошибкой «Run-Time Check Failure #0 — The value of ESP was not properly saved across a function call».
Стал разбираться…
Пришел к вот такому коду, который падает абсолютно по той же причине:
#include <string.h>
void test()
{
char a[10];
char b[] = "test";
strcpy_s(a, b);
__try
{
}
__except(1)
{
}
}
int main()
{
test();
return 0;
}
Компилятор:
Intel® C++ Compiler for applications running on IA-32, Version 10.0 Build 20070426 Package ID: W_CC_P_10.0.025
Командная строка:
icl /c /Od /RTC1 /MDd /Gd main.cpp
xilink main.obj
После разбора того, во что это всё компилируется, нашлась вот такая проблема: при вызове функции test() выделяется сразу вся нужная память на стеке, esp сохраняется в стек, вызывается strcpy_s, esp увеличиваем на 8(освобождаем стековую память), восстанавливаем сохраненный esp, esp увеличиваем на объем выделенной памяти в начале, сравниваем esp с ebp -> Ошибка!
Листинг получился такой(оставлено только нужное место):
push -1
push OFFSET FLAT: _try_info_pack0
push OFFSET FLAT: __except_handler3
push eax
sub esp, 3Ch
mov DWORD PTR [ebp - 18h], esp
add esp, 0
call strcpy_s
add esp, 8
mov esp, DWORD PTR [ebp - 18h]
add esp, 44h
cmp ebp, esp
call __RTC_CheckEsp
т.е. аналогия такая:
push eax;
mov dword ptr[ebp - 18h], esp;
pop eax;
mov esp, dword ptr[ebp - 18h];
cmp esp, ebp; // !!!!
Проблема решилась вот таким образом: в самое начало функции test() можно добавить такую вставку:
__asm
{
nop;
}
После этого ошибка пропадает, из листинга тоже видно, что код теперь корректен(стековая память выделяется перед вызовом strcpy_s, освобождается сразу после):
push -1
push OFFSET FLAT: _try_info_pack0
push OFFSET FLAT: __except_handler3
push eax
sub esp, 34h
mov DWORD PTR [ebp - 18h], esp
add esp, -8
call strcpy_s
add esp, 8
mov esp, DWORD PTR [ebp - 18h]
add esp, 44h
cmp ebp, esp
call __RTC_CheckEsp
т.е. аналогия такая:
mov dword ptr[ebp - 18h], esp;
push eax;
pop eax;
mov esp, dword ptr[ebp - 18h];
cmp esp, ebp; // Теперь всё хорошо
Думается мне, что это ошибка компилятора? Или ошибка закралась где-то у меня в коде?
P.S.
В версии компилятора 11.1.035 компилируется корректно, т.е. есть подозрение, что это именно косяк в компиляторе.
Added:
Следующий код падает по той же причине(при вызове любой функции, аргументы которой передаются через стек):
void t2(int a)
{
return;
}
void t1()
{
t2(10);
__try
{
}
__except(1)
{
}
}
int main()
{
t1();
return 0;
}