История изменений
Исправление wandrien, (текущая версия) :
Я как-то долго пытался понять, что не так с кодом, который работает на более новых компиляторах, но на gcc 4.x.x (сколько-то там, не помню точную версию) валит программу абсолютно без видимой причины. Дело под 32-битной системой было.
Код был приблизительно такой, схематически изображаю:
static int foo_real_impl(int* a, int* b, int c, int d /* тут на самом деле несколько разных типов, включая указатели на разные структуры */)
{
/* тут куча логики с вызовами разных функции,
которые вызывают еще кучу функций.
довольно глубокий уровень вложенности вызовов на стеке.
И где-то в глубине этого кода программа падала из-за обращения по битому указателю.
*/
}
int foo(int* a, int* b, int d)
{
return foo_real_impl(a, b, 0, d);
}
Так вот когда я пытался анализировать стек после падения, там были видны функции которые были глубже, чем foo()
, и также были видны те, который ближе к верхушке стека, чем foo()
. А самой foo()
не было.
Аргументы функций в глубине стека были частично бредовыми, было ясно, что там битые указатели и мусор.
В общем, я был вообще не уверен, что раскрутка стека отображается верно. Или может сам стек уже битый в момент падения.
В итоге я выписал хекс-дамп стека в текстовый файлик и пошел вручную из самой глубины расставлять комменты в нём, где какой фрейм, где какой аргумент и какая переменная.
В итоге пришел к тому, что что-то не так с кодом foo()
, потому что именно она портит аргументы. Дизассемблировал функцию и обнаружил что да - что-то не так.
Компилятор, видя что, что foo_real_impl()
- это static
, решил присвоить ей соглашение о вызове fastcall и передать часть аргументов в регистрах. Далее при генерации кода для foo()
он применяет оптимизацию, аналогичную хвостовой рекурсии, когда он не вызывает вложенную функцию, а замещает ею текущую функцию на стеке.
Поэтому foo()
на стеке и не видна была.
Но генерируемый код, который аргументы должен в правильном порядке разложить, содержал ошибку, из-за которой в один из указателей попадало неверное значение.
В общем, на ошибку в компиляторе наткнулся.
Исправление wandrien, :
Я как-то долго пытался понять, что не так с кодом, который работает на более новых компиляторах, но на gcc 4.x.x (сколько-то там, не помню точную версию) валит программу абсолютно без видимой причины. Дело под 32-битной системой было.
Код был приблизительно такой, схематически изображаю:
static int foo_real_impl(int* a, int* b, int c, int d /* тут на самом деле несколько разных типов, включая указатели на разные структуры */)
{
/* тут куча логики с вызовами разных функции,
которые вызывают еще кучу функций.
довольно глубокий уровень вложенности вызовов на стеке.
И где-то в глубине этого кода программа падала из-за обращения по битому указателю.
*/
}
int foo(int* a, int* b, int d)
{
return foo_real_impl(a, b, 0, d);
}
Так вот когда я пытался анализировать стек после падения, там были видны функции которые были глубже, чем foo()
, и также были видны те, который ближе к верхушке стека, чем foo()
. А самой foo()
не было.
Аргументы функций в глубине стека были частично бредовыми, было ясно, что там битые указатели и мусор.
В общем, я был вообще не уверен, что раскрутка стека отображается верно. Или может сам стек уже битый в момент падения.
В итоге я выписал хекс-дамп стека в текстовый файлик и пошел вручную из самой глубины расставлять комменты в нём, где какой фрейм, где какой аргумент и какая переменная.
В итоге пришел к тому, что что-то не так с кодом foo()
, потому что именно она портит аргументы. Дизассемблировал функцию и обнаружил что да - что-то не так.
Компилятор, видя что, что foo_real_impl()
- это static
, решил присвоить ей соглашение о вызове fastcall. далее при генерации кода для foo()
он применяет оптимизацию, аналогичную хвостовой рекурсии, когда он не вызывает вложенную функцию, а замещает ею текущую функцию на стеке.
Поэтому foo()
на стеке и не видна была.
Но генерируемый код, который аргументы должен в правильном порядке разложить, содержал ошибку, из-за которой в один из указателей попадало неверное значение.
В общем, на ошибку в компиляторе наткнулся.
Исправление wandrien, :
Я как-то долго пытался понять, что не так с кодом, который работает на более новых компиляторах, но на gcc 4.x.x (сколько-то там, не помню точную версию) валит программу абсолютно без видимой причины. Дело под 32-битной системой было.
Код был приблизительно такой, схематически изображаю:
static int foo_real_impl(int* a, int* b, int c, int d /* тут на самом деле несколько разных типов, включая указатели на разные структуры */)
{
/* тут куча логики с вызовами разных функции,
которые вызывают еще кучу функций.
довольно глубокий уровень вложенности вызовов на стеке.
И где-то в глубине этого кода программа падала из-за обращения по битому указателю.
*/
}
int foo(int* a, int* b, int d)
{
return foo_real_impl(a, b, 0, d);
}
Так вот когда я пытался анализировать стек после падения, там были видны функции которые были глубже, чем foo()
, и также были видны те, который ближе к верхушке стека, чем foo()
. А самой foo()
не было.
Аргументы функций в глубине стека были частично бредовыми, было ясно, что там битые указатели и мусор.
В общем, я был вообще не уверен, что раскрутка стека отображается верно. Или может сам стек уже битый в момент падения.
В итоге я выписал хекс-дамп стека в текстовый файлик и пошле вручную из самой глубины расставлять комменты в нём, где какой фрейм, где какой аргумент и какая переменная.
В итоге пришел к тому, что что-то не так с кодом foo()
, потому что именно она портит аргументы. Дизассемблировал функцию и обнаружил что да - что-то не так.
Компилятор, видя что, что foo_real_impl()
- это static
, решил присвоить ей соглашение о вызове fastcall. далее при генерации кода для foo()
он применяет оптимизацию, аналогичную хвостовой рекурсии, когда он не вызывает вложенную функцию, а замещает ею текущую функцию на стеке.
Поэтому foo()
на стеке и не видна была.
Но генерируемый код, который аргументы должен в правильном порядке разложить, содержал ошибку, из-за которой в один из указателей попадало неверное значение.
В общем, на ошибку в компиляторе наткнулся.
Исходная версия wandrien, :
Я как-то долго пытался понять, что не так с кодом, который работает на более новых компиляторах, но на gcc 4.x.x (сколько-то там, не помню точную версию) валит программу абсолютно без видимой причины. Дело под 32-битной системой было.
Код был приблизительно такой, схематически изображаю:
static int foo_real_impl(int* a, int* b, int c, int d /* тут на самом деле несколько разных типов, включая указатели на разные структуры */)
{
/* тут куча логики с вызовами разных функции,
которые вызывают еще кучу функций.
довольно глубокий уровень вложенности вызовов на стеке.
И где-то в глубине этого кода программа падала из-за обращения по битому указателю.
*/
}
int foo(int* a, int* b, int d)
{
return foo_real_impl(a, b, 0, d);
}
Так вот когда я пытался анализировать стек после падения, там были видны функции которые были глубже, чем foo()
, и ближе к верхушке стека, чем foo()
. А самой foo()
не было.
Аргументы функций в глубине стека были частично бредовыми, было ясно, что там битые указатели и мусор.
В общем, я был вообще не уверен, что раскрутка стека отображается верно. Или может сам стек уже битый в момент падения.
В итоге я выписал хекс-дамп стека в текстовый файлик и пошле вручную из самой глубины расставлять комменты в нём, где какой фрейм, где какой аргумент и какая переменная.
В итоге пришел к тому, что что-то не так с кодом foo()
, потому что именно она портит аргументы. Дизассемблировал функцию и обнаружил что да - что-то не так.
Компилятор, видя что, что foo_real_impl()
- это static
, решил присвоить ей соглашение о вызове fastcall. далее при генерации кода для foo()
он применяет оптимизацию, аналогичную хвостовой рекурсии, когда он не вызывает вложенную функцию, а замещает ею текущую функцию на стеке.
Поэтому foo()
на стеке и не видна была.
Но генерируемый код, который аргументы должен в правильном порядке разложить, содержал ошибку, из-за которой в один из указателей попадало неверное значение.
В общем, на ошибку в компиляторе наткнулся.