【问题标题】:Stack overflow on subroutine call only when compiled with Intel Visual Fortran and fine when compiled by Compaq Visual Fortran仅在使用 Intel Visual Fortran 编译时子程序调用堆栈溢出,在 Compaq Visual Fortran 编译时正常
【发布时间】:2012-04-26 04:24:11
【问题描述】:

对 Fortran .dll 使用相同的源文件,我可以使用 Compaq Visual Fortran 6.6C 或 Intel Visual Fortran 12.1.3.300 (IA-32) 编译它们。问题是在 Intel 二进制文件上执行失败,但在 Compaq 上运行良好。我在 Windows 7 64 位系统上编译 32 位。 .dll调用驱动写在C#中。

当调用内部子例程(从 .dll 入口例程调用)时,失败消息来自可怕的_chkstk() 调用。 (所以回答chkstk()

有问题的过程被声明为(请原谅固定的文件格式)

  SUBROUTINE SRF(den, crpm, icrpm, inose, qeff, rev,  
 &               qqmax, lvtyp1, lvtyp2, avespd, fridry, luin,  
 &               luout, lurtpo, ludiag, ndiag, n, nzdepth, 
 &               unit, unito, ier)

  INTEGER*4 lvtyp1, lvtyp2, luin, luout, lurtpo, ludiag, ndiag, n, 
 &          ncp, inose, icrpm, ier, nzdepth
  REAL*8    den, crpm, qeff, rev, qqmax, avespd, fridry           
  CHARACTER*2  unit, unito

并像这样调用:

      CALL SRF(den, crpm(i), i, inose, qeff(i), rev(i),  
 &             qqmax(i), lvtyp1, lvtyp2, avespd, fridry, 
 &             luin, luout, lurtpo, ludiag, ndiag, n, nzdepth,  
 &             unit, unito, ier)

除了crpmqeffrevqqmax 之外,具有类似变量规范的数组是每个SRF() 调用仅使用i-th 元素的数组。

如果参数的大小超过 8kb,我了解可能的堆栈问题,但在这种情况下,7 x real(64) + 11 x int(32) + 2 x 2 x char(8) = 832 bits 仅在传递的参数中。

我非常努力地将参数(尤其是数组)移动到模块中,但我一直收到同样的错误

.

Intel .dll 的反汇编是

Compaq .dll 的反汇编是

任何人都可以就导致 SO 的原因或如何调试它提供任何建议吗?

PS。我已将保留的堆栈空间增加到数百个Mb,但问题仍然存在。我尝试在反汇编程序中跳过chkstk() 调用,但程序崩溃了。堆栈检查从地址0x354000 开始,并向下迭代到0x2D2000,在该地址访问保护页时崩溃。栈底地址为0x282000

【问题讨论】:

  • 您在每种情况下都使用哪些编译器选项?尝试编译将所有你能想到的警告错误标志都启用。作为第一步,我倾向于将-std -check all -Warn all,nodec,interfaces,declarations -gen_interfaces -g -C -traceback -fpe0 -fp-stack-check 与 ifort 一起使用。
  • 我希望您现在已经发现了自己的算术错误,但是您的 832 字节总数应该是 832 位。也并非总是这样,每个字符都由一个字节表示:这往往因编译器和平台而异。使用最新的编译器,各种“存储单元”的大小可以作为 ISO_FORTRAN_ENV 内部模块中定义的常量使用。
  • 上面的两个 cmets 都给了我一些可以使用的东西。我会进行更多调查。可能存在CVF 没有捕获的堆栈损坏并且以某种方式将其吹倒。自从 80 年代首次编写代码以来,许多开发人员都接触过代码,代码中有很多内容。
  • 作为更新,到目前为止我尝试过的所有技巧和更改都没有让我更接近解决方案。我已将参数的数量和大小减少到 8 个标量,但问题仍然存在。
  • _chkstk() 检查是否有足够的堆栈空间用于局部变量,而不是参数(它们已经在堆栈中)。您是否偶然在子程序本地有大型数组?默认情况下,英特尔 Fortran 不对本地数组进行堆分配。

标签: arguments fortran intel-fortran


【解决方案1】:

你正在射击信使。 Compaq 生成的代码调用_chkstk(),不同的是它内联了它。常见的优化。两个 sn-ps 的主要区别在于:

 mov eax, 0D3668h

 sub esp, 233E4h

您在此处看到的值是函数所需的堆栈空间量。 Intel 代码需要 0xd3668 字节 = 865869 字节。 Compaq 代码需要 0x233e4 = 144356。差别很大。在这两种情况下,这都是相当大的数量,但英特尔的一个变得至关重要,一个程序通常有一个 1 兆字节的堆栈。吞下 0.86 兆字节的内容非常接近,嵌套了几个函数调用,然后您正在查看该站点的名称。

你需要找出什么,我无法帮助,因为它不在你的 sn-p 中,这就是为什么 Intel 生成的函数需要这么多空间来存放它的局部变量。解决方法是使用免费存储为大型数组寻找空间。或者使用链接器的 /STACK 选项来请求更多的堆栈空间(猜测选项名称)。

【讨论】:

  • 感谢您的信息。您能否详细说明使用免费商店 来寻找空间?在这个数组中发生了很多计算,填充了一个由 REAL*8 的 18,000,000 个元素组成的数组。
  • 我已经很老了,但还不够老。查看您最喜欢的 Fortran 参考资料中的 ALLOCATE 和 DEALLOCATE 语句。
  • 这些数组在 VBA 中分配并传递给 Fortran .dll。它们没有在 Fortran 中分配,所以我不能使用可分配数组。不过,该帖子非常有帮助,因为我仍在寻找答案。
【解决方案2】:

问题不在于发生堆栈溢出的函数调用。

在代码的早期,有一些全局矩阵被初始化并被放置在堆栈中,由于代码中的错误,它们仍在范围内并且已经几乎填满了堆栈。当函数调用发生时,编译器试图将返回地址存储到堆栈中,导致程序崩溃。

解决方案是制作全局矩阵allocatable,并确保“堆数组”选项设置为适当的值。

这真是个兔子洞,当它 100% 是我的错误代码导致问题时。

【讨论】:

    猜你喜欢
    • 2012-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-28
    • 2018-11-05
    • 1970-01-01
    • 2012-04-15
    相关资源
    最近更新 更多