4

I want to call a function that takes as an argument a derived type from within the Gnu Debugger (gdb). I eventually get a segfault. Here's a "minimum" working example:

module modSimpleClass
    type Person
        character(len=20) :: Name
        contains
            procedure PrintName
    end type
    contains
        subroutine PrintName(a)
            class(Person) :: a
            print *, a%Name
        end subroutine
        subroutine Print42()
            print *, "42"
        end subroutine
        subroutine PrintNameFromOutside(a)
            class(Person) :: a
            print *, a%Name
        end subroutine
end module
program testgdb
    use modSimpleClass
    implicit none
    type(Person) :: JoeJohnson

    JoeJohnson%Name = "Joe Johnson"
    call JoeJohnson%PrintName
end program testgdb

I compile it with gfortran version 9.3.0, run it from gdb and stop it before its end. The following things work:

call Print42()
print JoeJohnson%Name

The following things don't work:

call JoeJohnson%PrintName()!There is no member named PrintName.
call PrintName(JoeJohnson)!Error then segfault
call PrintNameFromOutside(JoeJohnson)!Error then segfault

The error is as follows:

The program being debugged was signaled while in a function called from GDB. GDB remains in the frame where the signal was received. To change this behavior use "set unwindonsignal on". Evaluation of the expression containing the function (modsimpleclass::printname) will be abandoned. When the function is done executing, GDB will silently stop.

How to correctly call fortran subroutines from gdb?


Thank you for you response @Flusslauf. I am going to try to rephrase what you said:

From the point of view of gfortran, if we have the derived type Person defined in module modsimpleclass,

  • Only the scope in which the variable is initialized sees the original type.
  • Any function or subroutine which takes as an argument an object of that derived type sees the type __class_modsimpleclass_Person_t with attributes:
    1. _data: A pointer to the object.
    2. _vptr: A pointer to something that has information about the object.

So a function really takes two pointers for each object of a derived type: one which points to the object and another one which points to information about the object.

From gdb, passing to a function a pointer to the object works, but passing to it the object itself doesn't work.


@Flusslauf established

call PrintName( &JoeJohnson )!works because it is equivalent to
call PrintName( a%_data )!this, which works too
call PrintName( a )!this works and it makes sense
call PrintName( a%_data, a%_vptr )!this also works
call PrintName( a%_vptr)!this outputs something strange

call PrintName( *(a%_data) )!causes segfault since equivalent to
call PrintName( JoeJohnson )!Error then segfault

, where each instruction is executed in the correct scope for the variable.

  • 1
    This *looks* like it's not supported by GDB, you could open a bug report at https://sourceware.org/bugzilla/ – philb Dec 31 '21 at 17:23
  • 1
    Passing the object would make gdb interpret it as a pointer (since that is what is expected here) or rather a pointer pair I think. GDB then tries to dereference that and lands somewhere in memory where it is not supposed to be which should lead to a SIGSEGV. – Flusslauf Jan 12 '22 at 16:59
  • 1
    Generally I think this conclusion is correct (for this version of gfortran). Passing the pointer of the object only works because _data is the actually passed __class_modsimpleclass_Person_t object's first member and _vptr (sadly I don't know what that is) is not dereferenced and used, I guess. Since technically one is not handing an address pointing to __class_modsimpleclass_Person_t over to the function. Would _vptr be used also this call should lead to a SIGSEV. – Flusslauf Jan 12 '22 at 17:09
  • 1
    @Flusslauf I added my compiler version to the question (gfortran 9.3.0) since this is compiler dependent. I also tried stopping inside the function and making different calls. Most of them worked and agree with your previous comments. I added what I tried to my question if you are curious. – Mehdi Slimani Jan 14 '22 at 12:04

1 Answers1

2

The problem here is that while the normal Fortran syntax allows the call PrintName(JoeJohnson) the actual type of the passed argument seems to be (setting a breakpoint within the printname and compiling gfortran)

Breakpoint 1, modsimpleclass::printname (a=...) at ./stack.f90:10
10                  print *, a%Name
(gdb) ptype a
type = Type __class_modsimpleclass_Person_t
    PTR TO -> ( Type person :: _data )
    PTR TO -> ( Type __vtype_modsimpleclass_Person :: _vptr )
End Type __class_modsimpleclass_Person_t

while the type of JoeJohnson is of non-pointer type. I guess the above type is gfortran's way of passing type arguments in Fortran.

In the above case

PrintName(&JoeJohnson)

would work but is hacky..


Edit:

Just for completion - ifx/ifort actually seem to use a pointer for the argument passing

(gdb) ptype a
type = PTR TO -> ( Type person
character*20 :: name
    void :: printname(void)
End Type person )

so doing call modsimpleclass::printname(&JoeJohnson) would not be as hacky anymore (GDB is not able to resolve PrintName when compiling with ifx/ifort). I am not sure whether one could adapt gdb in a meaningful way to allow for this kind of calling. Since the parameter handling and calling syntax is compiler dependent.

Flusslauf
  • 102
  • 12
  • 2
    It is often said that Fortran passes all arguments by reference. While it is not exact, it is true in normal circumstance. Pointers are used also to pass normal arguments of simple numeric types. – Vladimir F Героям слава Jan 12 '22 at 10:40
  • 1
    Thanks a lot for you response @Flusslauf. I think I understood a bit more. I wanted to write a long comment with what I understood from your answer, but it was too long. I put the long comment at the end of my question. – Mehdi Slimani Jan 12 '22 at 15:22
  • 1
    Thank you for your edit. I had previously missed that this is compiler dependent. – Mehdi Slimani Jan 14 '22 at 10:51
  • 2
    Both compilers pass an address. Fortran is really typically [pass-by-reference](https://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value). – Vladimir F Героям слава Jan 14 '22 at 12:00
  • @VladimirF From what I understood gfortran passes 2 addresses and ifor passes a single address. Moreover gfortran creates an internal type for every derived type to do its business. – Mehdi Slimani Jan 14 '22 at 13:04
  • 1
    @MehdiSlimani I misseds the polymorphsm here. I just confirmed that in simple subroutines code that passes derived types works fine together even if part is compiled using ifort and part by gfortran. Of course, one cannot use modules. But polymorphism, that means `class`, can indeed be implemented differently. And certainly will be compiler dependent. – Vladimir F Героям слава Jan 14 '22 at 13:18
  • @VladimirF I actually don't know how to use the keyword ```type``` instead of ```class``` in my methods. I always get a ```Non-polymorphic passed-object dummy argument``` error. Polymorphism is a concept I don't understand. I'll do some reading. – Mehdi Slimani Jan 14 '22 at 13:24