Запутанное поведение стека в программе, скомпилированной G77

У меня есть смесь C++ и Fortran 77, скомпилированная с помощью G77. В основном это C++, но он вызывает решатель ODE DVERK, который затем обращается к глобальной функции C для получения производной (объявленной с помощью __stdcall). Все работало нормально, пока я не получил странный SIGSEGV.

Я отследил это, потому что esp уменьшался ниже границы своего сегмента. Причина, по которой это было сделано, заключается в том, что DVERK содержит оператор **, который внутри представляет собой вызов функции с двумя значениями двойной точности, передаваемыми по значению в стеке, для 16 байтов. Функция возвращает ответ, но потом я вижу эту инструкцию:

sub 0x10,%esp

который уменьшает указатель стека на 16 байт, как если бы он возвращал аргументы обратно в стек (?). Похоже, что G77 делает это после каждого вызова функции, и обычно это не причиняет вреда, поскольку указатель стека не изменяется. Однако в случае ** указатель стека остается уменьшенным, и если этот код выполняется достаточное количество раз, уменьшение увеличивается до тех пор, пока я не получу SIGSEGV. (Если в стеке достаточно места, эта проблема не появляется, потому что возврат от DVERK устраняет ее.)

Я попытался заменить код a**b на dexp(dlog(a)*b), но происходит то же самое, за исключением того, что это происходит в два этапа, 8 байтов после dlog и 8 байтов после dexp.

Должно быть что-то, что я делаю неправильно при настройке соглашений о вызовах, используемых G77, по отношению к его библиотеке времени выполнения. Экспертиза приветствуется.


person Mike Dunlavey    schedule 06.09.2014    source источник


Ответы (1)


В эту эпоху способ смешивания C или C++ с Fortran заключается в использовании ISO-C_Binding. Из C++ вызовите с помощью extern C. В Fortran используйте (английское слово) привязку Fortran ISO C для вызываемой процедуры. Это заставит Fortran использовать соглашение о вызовах C (ABI). Тогда вам не нужно разбираться во внутренностях компилятора. Для этого вам придется использовать более современный компилятор Фортрана, чем g77, например, gfortran. gfortran полностью способен компилировать FORTRAN 77. Официально ISO-C-Binding является частью Fortran 2003 — вы можете написать оболочку между C++ и существующим FORTRAN 77.

Существует тег Stackoverflow, объясняющий привязку ISO-C: https://stackoverflow.com/tags/fortran-iso-c-binding/info

person M. S. B.    schedule 07.09.2014
comment
Эра? :) У меня есть внешняя часть C, которая отлично работает. Я не решился на gfortran, главным образом потому, что по этому пути есть тонна технического долга. Но спасибо за информацию. Я проверю это. - person Mike Dunlavey; 07.09.2014
comment
Чтобы перейти с g77 на gfortran, не так уж много нужно понять. Возможно, параметров компилятора -ffixed-form -ffixed-line-length-none будет достаточно для компиляции существующего кода FORTRAN 77. - person M. S. B.; 07.09.2014
comment
@MikeDunlavey Я бы даже попробовал переключиться на gfortran и оставить другие изменения, если этого недостаточно. Вполне мог. - person Vladimir F; 08.09.2014
comment
Но имейте в виду, что простое использование модуля iso_c_binding не меняет соглашения о вызовах. Атрибут bind(C) делает это. Обертки Usefel Fortran-C можно делать и без этого модуля. - person Vladimir F; 08.09.2014
comment
Спасибо за предложение. Я еще не готов погрузиться в gfortran. Я выяснил причину, по которой DVERK звонил ** миллионов раз - это была ошибка в моем коде. Это устранило проблему, потому что, если ** вызывается всего несколько сотен раз, он не тратит впустую достаточно стека, чтобы исчерпать сегмент. Но я ценю твой ответ. - person Mike Dunlavey; 08.09.2014