Compiling, linking, using Shared Library

Hello everyone! I am using Debian 12 (amd64) with gfortran 12.2 and gmake. I want to be able to create a shared library and then be able to link to this library to a new fortran program which is located in a directory elsewhere. I have been able to create the shared library and associated modules but I am having trouble calling the library’s subroutines and functions from the new program. Here are the steps I followed:

  1. Create and Compile Shared Library
    One of the files in this library is constants.f03:
module dnx_constants
   use iso_fortran_env, only: sp => real32, dp => real64
   implicit none
   real(dp), parameter :: dnx_pi=3.14159265358979323846d0
end module dnx_constants

Another file is vector.f03 and it is basically a module which contains a few subroutines:

module dnx_vector
   use dnx_constants
   implicit none
   public :: dnx_vprint

contains

   subroutine dnx_vprint(v, vname)
      implicit none
      integer :: i, n
      real(dp), intent(in), dimension(:) :: v
      character(len=8), intent(in) :: vname

      n= size(v)
      write(*,*)
      write(*,*) vname
      do i=1,n,1
         print*, v(i)
      end do
      write(*,*)

   end subroutine dnx_vprint
end module dnx_vector ! [EDIT] added missing line

I compiled the library as follows:

gfortran -c -std=f2008 -fPIC constants.f03 vector.f03
gfortran -shared constants.o vector.o -o libdnx.so

Compiles fine with no issues. Generates the .so as well as a couple .mod files.

  1. Creating and compiling a new program which uses the library (main.f03):
program main
   use dnx_constants
   use dnx_vector
   implicit none
   print*, dnx_pi
   ! call dnx_vprint((/1.d0, 1.d0, dnx_pi/),"v=      ")
end program main    

Compilation and linking are as follows

LIBDIR= /path/to/library
gfortran -L$(LIBDIR) -I$(LIBDIR) -ldnx main.f03 -o main.run

LD_LIBRARY_PATH environmental variable points to LIBDIR. Program compiles fine and it prints out the value of dnx_pi to the screen. Uncommenting the call to dnx_vprint gives me this error during compilation however:

/usr/bin/ld: /tmp/ccPeSQlf.o: in function `MAIN__':
main.f03:(.text+0x101): undefined reference to `__dnx_vector_MOD_dnx_vprint'

Any help would be appreciated.

Obtaining the value of pi was done at compile time - so doesn’t depend on finding the shared library at run time. However calling the procedure does require access to the shared lib. So somehow you’ve probably lost your LD_LIBRARY_PATH environment variable.

What happens when you type ‘ldd main.run’? It should list where it is looking for the various shared libs.

Note that you can hard code the path to the shared lib into the main program at link time. Use the -Wl,-rpath option. Then you don’t have to worry about setting LD_LIBRARY_PATH at run time.

Two/three comments:

  • Your source code is about Fortran in free form syntax. Regardless if you adopt the standard of Fortran 90, or newer, the general recommendation is to use .f90 as file extension.
    On the contrary, .f typically is used for the fixed format of FORTRAN77 (comments indicated by C; instructions (except labels and continuation signs ) constrained to anything between char 7 and 72 per line, etc.). A compiler like gfortran for instance assumes example.f is the elder format, equivalent to the explicit -std=legacy, or -ffixed-form compiler flag. Capitalization of the file extension (.F90, or .F) conventionally indicates the source code shall pass a pre-processor prior to compilation (and linking).

  • The representation of vector.f03 is incomplete; without the closing line end module dnx_vector (or at least an end module) gfortran’s work doesn’t complete.

  • On occasion, I see the approximation of of \pi in lines of

    program define_pi
       use iso_fortran_env, only: dp => real64
       real(dp), parameter :: pi = 4.0_dp * atan(1.0_dp)
       print *, pi
    end program define_pi
    

    which may easier written down than the long string (if precision suffices).

I am now compiling using -Wl,-rpath /link/to/library

LDD Gives me the following:

$ ldd main.run 
	linux-vdso.so.1 (0x00007ffc28790000)
	libgfortran.so.5 => /lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007f8491600000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f849141f000)
	libquadmath.so.0 => /lib/x86_64-linux-gnu/libquadmath.so.0 (0x00007f849196d000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8491340000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f849194d000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f84919d2000)

Still getting the same error when I uncomment the call to dnx_vector:

main.f90:(.text+0x101): undefined reference to `__dnx_vector_MOD_dnx_vprint'

Note that I can link with the shared lib and call the subroutine just fine if it is NOT contained inside of a module and its just sitting by itself outside of a module. Perhaps the feature I am looking for does not exist in GCC?

Any other thoughts?

It may be useful to place the link part after main.f03, i.e., instead of

put -ldnx after main.f03:

gfortran -L$(LIBDIR) -I$(LIBDIR) main.f03 -ldnx -o main.run

Indeed, the order of libraries seems to matter for GCC according to the following pages:

But the result may depend on OS / platforms, so maybe try… (Also, any order of libraries may be OK for other compilers).

1 Like

Replace the blank character with a comma. E.g., -Wl,-rpath,/link/to/library

And yes, you should place the -ldnx after the main.f03. So in your makefile, it would look like:

gfortran -I$(LIBDIR) -L$(LIBDIR) main.f03 -ldnx -o main.run -Wl,-rpath,$(LIBDIR)

If you then do a ldd main.run, you should see something like:

$ ldd main.run
	linux-vdso.so.1 (0x00007ffca5d6c000)
	libdnx.so => /link/to/library/libdnx.so (0x00007f31deffc000)
	libgfortran.so.5 => /lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007f31ded1e000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f31debcf000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f31debb4000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f31de9c2000)
	libquadmath.so.0 => /lib/x86_64-linux-gnu/libquadmath.so.0 (0x00007f31de978000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f31df003000)
1 Like

wspector, septc

Worked perfectly! The flag -ldnx goes AFTER main.f03 (order matters) and -Wl,-rpath, to point to the library location.

Thanks!

2 Likes