导致GOTO指令分段错误的原因是什么?

时间:2019-08-23 08:32:33

标签: memory fortran segmentation-fault goto

我正在研究一个很旧的fortran 77代码,称为LOWTRAN。 它基本上是用于模拟大气光传播的仿真工具。
(如果您希望查看完整的lowtran代码,可以查看here,尽管我认为这对回答问题没有帮助。)

不幸的是,由于该代码最初是为打孔卡制作的,因此已被改编为现代输入/输出方法,并产生了一些令人讨厌的故障。
这些故障很容易发现/难以修复。
为了修复其中之一,我别无选择,只能设置一个IF语句,该语句包含一个IF语句之外的GOTO,代码中还有其他内容。

但是,有时,GOTO本身会导致分段错误。它不是随机发生的,而是取决于一些与该IF语句无关的变量。

我正在两台不同的计算机上编译该项目,其中一台没有segfault。两者都使用gfortran 在Windows机器(没有segfault的机器)上,我使用gfortran 7.2.0;在Linux机器(有segfaults的机器)上,我使用gfortran 4.8.5

(由于我没有所需的权限,因此我无法在linux计算机上更新gfortran版本)

请注意,当我编译修订时,两个编译器显然都会发出警告:

Warning: Legacy Extension: Label at (1) is not in the same block as the GOTO statement at (2)

这是解决方法

100
...
...
<Lots of code>
...
...
   if(ierror.eq.-1) then
       itype = 1
       ierror = 0
       go to 100               
   end if

3 个答案:

答案 0 :(得分:0)

计算机运行的代码不是您的源代码,而是机器代码。编译器从您的源代码生成该机器代码。生成或多或少是直接的,因此源代码的一条语句对应于一些连续的机器代码指令。但这不一定是直接的。特别是,如果编译器提供了 optimizations ,则源代码行与机器代码指令之间的对应关系可能会中断。在这种情况下,debugger报告为location of the SEGV的行可能是错误的。

GOTO语句的简单实现是无条件的跳转机器代码指令,跳转到有效的代码地址。这种简单的实现将永远不会产生SEGV。您可能很想将编译器的错误归咎于编译器,但是that would be a mistake。编译器优化可能使事情变得混乱。您可能在GOTO语句 near 附近的数组访问或目标之后的代码(标记为100的语句)中有错误。

尝试使用optimizations turned off(通常使用-O0之类的命令行选项)重新编译程序,然后重新运行程序。然后,您应该在无效阵列访问的行上看到SEGV报告。

答案 1 :(得分:0)

多亏了Raedwald,我才能够找到实际发生的事情。

编译器优化可以“隐藏”分段错误的真正原因。

实际发生的事情是,有一个巨大的循环正在将标签100用作其终点的参考。有时,标签100上的GOTO导致循环重复执行一次,从而导致数组违反acces。

我通过定义新标签解决了这个问题。

我从没想过禁用编译器优化,这真的很有帮助。

答案 2 :(得分:0)

如果遇到与内存相关的错误,总是很费力-没有捷径。我可以想象,这与您在此处所举的示例类似。在大多数情况下,与代码中的某些部分的jumping有关的错误非常重要。

      program main
      implicit none
c
      call hello
      end

      subroutine hello
      implicit none
      integer a, i
      integer, dimension(:), allocatable :: x
      allocate(x(100))

      goto 101

100   do i = 1, 100
        x(i) = i
      end do
      return

101   read(*,*) a
c
      write(*,*) a

      if (a.eq.-1) then
        deallocate(x)
        go to 100
      end if

      go to 100
c
      end

对于调试器,我建议使用gdb(在Linux上应该在那里)。这样可以更轻松地找到问题。

有时SIGSEGV会被一个讨厌的字节“触发”。因此,很难被钉住。另外,请记住,此类错误通常是“ Heisenbug”类型的:https://en.wikipedia.org/wiki/Heisenbug

更新

上面的代码是@Raedwald关于优化的建议的完美例证。

> gfortran -O0 -o main main.f
> ./main
-1
          -1

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Backtrace for this error:
#0  0x2B7311C376F7
#1  0x2B7311C37D3E
#2  0x2B73126C926F
#3  0x400C1A in hello_
#4  0x400C95 in MAIN__ at main.f:?
Segmentation fault
> gfortran -O3 -o main main.f
> ./main
-1
          -1
>