Search code examples
fortranlinker-errorsmpichcgns

Fortran undefined reference when library is loaded and contains the sought after symbol


I have modules compiled on their own and then linked in an executible to form a program. I am calling symbols from the cgns library, but some of them throw an undefined reference error, even though the symbol exists in the library.

This code seems to compile fine in older gcc ~4 to 8 versions, but not my current version 13. I am also using the mpif90 compiler from mpich.

So I recreated the problem with a dummy code. Here is my setup with a dummy code that recreates the issue exactly:

  • Fedora 39
  • GCC 13 via dnf install gcc gfortran
  • MPICH 4.1 via dnf install mpich-devel
  • CGNS 4.4 via dnf install cgnslib-mpich-devel
  • HDF5 1.12.1 via dnf install hdf5-mpich-devel (not sure if relevant here)

The reference in question is cg_nbases_f, so doing

me@pc:dir$ objdump -T $MPI_LIB/libcgns.so | grep cg_nbases_t
00000000000a44b0 g    DF .text  0000000000000055  Base        cg_nbases_f

The dummy code consists of two files, a module cgnsreading.f90 and a program cgnsread.f90

cgnsreading.f90:

module cgnsreading
    implicit none

    private

    public talk_to_cgns

contains

    subroutine error_check()
        print *, 'hi I am the bane of arthropods'
    end subroutine error_check

    subroutine talk_To_cgns()
        character(100) :: errmsg
        integer :: i = 1
        integer :: nbases = 1
        integer :: ierr

        call cg_get_error_f(errmsg)
        print *, errmsg ! Should print that there is no error
        call error_check() ! Should print its guts

        call cg_nbases_f(i,nbases,ierr) ! Throws undefined reference error

    end subroutine talk_To_cgns

end module cgnsreading

cgnsread.f90:

program cgnsread
    use cgnsreading
    implicit none

    call talk_to_cgns()
    
end program cgnsread

Compiling it and linking in the following way produces the error:

me@pc:dir$ mpif90 cgnsreading.f90 -c
me@pc:dir$ mpif90 cgnsread.f90 -c
me@pc:dir$ mpif90 cgnsreading.o cgnsread.o -lcgns -o cgnsread
/usr/bin/ld: cgnsreading.o: in function `__cgnsreading_MOD_talk_to_cgns':
cgnsreading.f90:(.text+0x95): undefined reference to `cg_nbases_f_'
collect2: error: ld returned 1 exit status

My thought was that there were either some issues related to the module having private subroutines, but that is ruled out because the error occures by calling the public subroutine.

I was also suspecting that it's due to the compiler ignoring the linking flag (flags order), but when that happens, cg_get_error_f throws the same error. Hence, why I tested that as well.

I expected that the references would only be available in the devel versions of the cgns library, but that doesn't seem to be the case because I am using the devel version.

There was a suspicion that a different library was installed somewhere else, and indeed there is, but it also contains the missing reference.

I do not know if this is a specific problem to cgns or its installation, but I haven't tested this with something else.

Why would a library provide a symbol but not the other, even though both exist in the same library.

The same error occures on another pc with the same setup as mentioned above. So the problem can be replicated.


Solution

  • Check the symbol names defined in the library:

    nm -D /usr/lib/x86_64-linux-gnu/libcgns.so | grep nbases
    
    0000000000047070 T cg_nbases
    0000000000084900 T cg_nbases_f
    

    The symbol that we are looking for is there, but the mpif90 compiler has appended an underscore to the name. In principle this is to prevent clashes between Fortran and C names.

    Update cgnsreading.f90 and add use cgns:

    module cgnsreading
        use cgns
        implicit none
    
        private
    
        public talk_to_cgns
    
    contains
    
        subroutine error_check()
            print *, 'hi I am the bane of arthropods'
        end subroutine error_check
    
        subroutine talk_to_cgns()
            character(100) :: errmsg
            integer :: i = 1
            integer :: nbases = 1
            integer :: ierr
    
            call cg_get_error_f(errmsg)
            print *, errmsg ! Should print that there is no error
            call error_check() ! Should print its guts
    
            call cg_nbases_f(i,nbases,ierr) ! Throws undefined reference error
    
        end subroutine talk_to_cgns
    
    end module cgnsreading
    

    When you compile that you'll need to specify the include path for cgns.mod, which should be located at /usr/include/cgns.mod.

    mpif90 cgnsreading.f90 -c -I/usr/include/
    mpif90 cgnsread.f90 -c
    mpif90 cgnsreading.o cgnsread.o -lcgns -o cgnsread
    

    enter image description here

    I don't have a Fedora 39 system but I can replicate with Docker. Follow these steps to get it set up.

    FROM fedora:39
    
    RUN dnf install -y wget gcc-gfortran hdf5-devel cgnslib openmpi openmpi-devel
    
    RUN wget https://dl.fedoraproject.org/pub/fedora/linux/releases/39/Everything/x86_64/os/Packages/c/cgnslib-devel-4.4.0-2.fc39.x86_64.rpm && \
        rpm -i cgnslib-devel-4.4.0-2.fc39.x86_64.rpm && \
        rm -f cgnslib-devel-4.4.0-2.fc39.x86_64.rpm
    
    ENV PATH=$PATH:/usr/lib64/openmpi/bin/
    
    COPY *.f90 .
    
    RUN mpif90 cgnsreading.f90 -c -I/usr/lib64/gfortran/modules/
    RUN mpif90 cgnsread.f90 -c
    RUN mpif90 cgnsreading.o cgnsread.o -lcgns -o cgnsread