Why am I getting a segmentation fault here (M1 Mac)?

I just invested in a new Mac with an M1 chip. I’m getting a segmentation fault for this code:

module mylib
  implicit none
  
  abstract interface 
    subroutine fcn_p
    end subroutine
  end interface
  
contains

  subroutine tester()
    real(8) :: a
    a = 20.d0
    call dumb(bla)  
    
  contains
    
    subroutine bla()
      print*,a
    end subroutine
    
  end subroutine
  
  subroutine dumb(fcn)
    procedure(fcn_p) :: fcn
    call fcn()
  end subroutine
  
end module

program main
  use mylib
  implicit none
  call tester()
end program

compile with

gfortran test.f90 -O3

result is…

zsh: segmentation fault  ./a.out

My installation of gfortran

GNU Fortran (Homebrew GCC 11.2.0_3) 11.2.0

I do not get a segmentation fault when I use no optimization (no -O3). Also no segmentation fault every on intel mac or on ubuntu virtual machine.

Thanks for any advice.

2 Likes

I don’t see a problem. If you add -fbacktrace -g do you still get the segfault? Do you get a traceback too?

1 Like

@urbanjost, compiling with gfortran mylib.f90 -O3 -fbacktrace -g still gives a segmentation fault.

1 Like

No traceback? I could be missing something, but looks like a compiler issue to me so far. If you have gdb it might be interesting to try to run it there; sticking in a few prints might help isolate the problem but I still do not see a problem on your side.

1 Like

Ya no traceback. Adding a few prints:

module mylib
  implicit none
  
  abstract interface 
    subroutine fcn_p
    end subroutine
  end interface
  
contains

  subroutine tester()
    real(8) :: a
    a = 20.d0
    print*,'hi1'
    call dumb(bla)  
    
  contains
    
    subroutine bla()
      print*,'hi3'
      print*,a
    end subroutine
    
  end subroutine
  
  subroutine dumb(fcn)
    procedure(fcn_p) :: fcn
    print*,'hi2'
    call fcn()
  end subroutine
  
end module

program main
  use mylib
  implicit none
  
  call tester()
  
end program

gives me

 hi1
 hi2
zsh: segmentation fault  ./a.out

The issue seems to be with the variable a. When I comment out print*,a within subroutine bla, then there is no segmentation fault. I do not see any mac m1 binaries of gdb. So i can’t try that very quickly.

1 Like

In my laptop (gfortran 11.2 on Windows 11), there is no problem as well.

$ gfortran main.f90 && ./a
 hi1
 hi2
 hi3
   20.000000000000000 
1 Like

No error on Ubuntu 20.04 with gfortran 9.3.0.

$ gfortran test.f90 && ./a.out 
 hi1
 hi2
 hi3
   20.000000000000000  
1 Like

The segfault could be due to the internal function bla(). Converting it to a regular procedure may resolve the segfault.

I do not know much about M1, but the segfault can be reproduced in Linux by marking the output binary as not requiring an executable stack (internal functions require an executable stack with gfortran).

gfortran-10 -cpp -DEXECSTACK_ENABLED segfault.f90; execstack -c ./a.out; ./a.out
Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Try this alternative code to see if this is indeed the source of error,

module mylib

    implicit none
    integer, parameter :: RK = kind(1.d0)

#if !EXECSTACK_ENABLED
    real(RK) :: a
#endif

    abstract interface
        subroutine fcn_p
        end subroutine
    end interface

contains

#if EXECSTACK_ENABLED
    subroutine tester()
        real(RK) :: a
        a = 20.d0
        call dumb(bla)
    contains
        subroutine bla()
          print*,a
        end subroutine
    end subroutine
#else
    subroutine tester()
        a = 20.d0
        call dumb(bla)
    end subroutine
    subroutine bla()
      print*,a
    end subroutine
#endif

    subroutine dumb(fcn)
        procedure(fcn_p) :: fcn
        call fcn()
    end subroutine

end module

program main
    use mylib
    implicit none
    call tester()
end program
gfortran-10 -cpp segfault.f90; ./a.out

   20.000000000000000 
3 Likes

Cannot find anything saying it is illegal, but that is pushing things a bit, where the procedure passed in is contained, which certainly makes the scope of A a bit convoluted. It would change the code quite a bit, but wondering if it goes away if you move the declaration of A (the REAL statement) before the first CONTAINS. If you put an IMPLICIT NONE in the BLA routine it does not (in my opinion, incorrectly) say A is not defined, does it? That would definitely be a bug but might give a hint about what it is doing wrong. Have to admit the logic here is not anything I have done but I see no reason it is not standard. Cannot say passing in the procedure to call and having it in a contained routine and using a variable it gets access to by being a contained procedure is a first for me.

The only (?) Fortran feature not yet working in gfortran for M1 are procedure pointers to internal subprograms: https://twitter.com/fxcoudert/status/1315356308703506432. I believe it is indeed related to an executable stack not being allowed. Note, that gcc for M1 is experimental and not yet officially included in gcc.

1 Like

@shahmoradi I tried your posted code. With -DEXECSTACK_ENABLED, the code crashes, and without, then the code works OK.

@mhulsen points out that nested functions are just not supported yet by gcc on M1. So this is a compiler bug, that gcc developers are aware of. I’ve found the relevant issue: Nested functions · Issue #26 · iains/gcc-darwin-arm64 · GitHub

also here iains says that fixing this bug is a priority. Thanks so much to these people!

4 Likes