Непонятное поведение стека в программе, скомпилированной 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, в сравнении с его библиотекой времени выполнения. Экспертиза ценится.
1 ответ
В эту эпоху способ смешать C или C++ с Fortran - это использовать ISO-C_Binding. Из C++ вызывайте с помощью extern C. В языке Fortran (англоязычное слово) привязка Fortran ISO C для вызываемой процедуры. Это заставит Fortran использовать соглашение о вызовах C (ABI). Тогда вам не нужно понимать внутренности компилятора. Для этого вам придется использовать более современный компилятор Fortran, чем g77, например, gfortran. gfortran полностью способен компилировать FORTRAN 77. Официально ISO-C-Binding является частью Fortran 2003 - вы можете написать оболочку между C++ и существующим FORTRAN 77.
Существует тег Stackru, объясняющий привязку ISO-C: https://stackru.com/tags/fortran-iso-c-binding/info