Shared Library in Fortran, Minimal Example Does Not Work

Shared library in Fortran, minimal example does not work

The only thing that has to be changed is the order of the arguments, as in

gfortran -L. main.f90 -llol -o main

Yes, only main.f90 and -llol are reversed. I hope this saves someone the year of his life I just lost on this. On a related note, if you are trying to compile a program which uses LAPACK or BLAS (which did not work for me and is why in the first place I tried to create a shared library myself), the same also applies. Write the name of the source file first:

gfortran mylapack.f90 -llapack -lblas -o mylapack

The reason for this can be found in the manual pages, see the top of http://gcc.gnu.org/onlinedocs/gcc/Link-Options.html for the option -l:

It makes a difference where in the command you write this option; the
linker searches and processes libraries and object files in the order
they are specified. Thus, foo.o -lz bar.o searches library z after
file foo.o but before bar.o. If bar.o refers to functions in `z',
those functions may not be loaded.

strange problem in building Fortran shared library with cmake

After a little inspection I figured that this happens because of runtime library mismatch. For compiling the code I used my locally installed gfortran but during the run I think ld grabs some libs from the gcc installed to a CONDA environment.

Error Intel Fortran calling C shared library function that receives an integer

You should not use an abstract interface for this, but the problem probably comes from not using bind(C).

Without bind(C) Intel Fortran compiles the function to expect a pointer (to a copy of something). To make it expect a value, use both value and bind(C).

Name mismatch when using Fortran shared library

Note that you have two sources that compile to a test.obj - the Fortran and the C. Also, the cl command you showed tries to generate an executable. Here's how to do it right:

cl /c /MD /Foctest.obj test.c
ifort /dll test.f90 ctest.obj
ifort example.f90 test.lib

I added /MD to the C compile to get a compatible set of run-time libraries. This generated the executable without errors.

Fortran dynamic libraries, load at runtime?

Here are some few links that can be helpfull:

  • This page on rosettacode.org which gives complete example with details and discuss implementation on linux and MACOS
  • This intel forum post where Steve Lionel give some advice on how to do the dynamic loading with ifort
  • this IBM page with a great explanation of dynamic libraries and their usage

If you want a small easy to understand code, keep reading. Few days ago, I was playing with dynamic loading. My test code below might be of help to you. However I work in the linux environment and you might have to adapt few thing here and there for it to work on your OS X environment. The rosettacode.org link above will come handy to help you.

Here is the code for the test dynamic lib

[username@hostname:~/test]$cat test.f90

module test
use, intrinsic :: iso_c_binding
contains
subroutine t_times2(v_in, v_out) bind(c, name='t_times2')
integer, intent(in) :: v_in
integer, intent(out) :: v_out
!
v_out=v_in*2
end subroutine t_times2
!
subroutine t_square(v_in, v_out) bind(c, name='t_square')
integer(c_int), intent(in) :: v_in
integer(c_int), intent(out) :: v_out
!
v_out=v_in**2
end subroutine t_square
end module test

Compiled as

[username@hostname:~/test]$gfortran -c test.f90
[username@hostname:~/test]$gfortran -shared -o test.so test.o

Here is the test program

[username@hostname:~/test]$cat example.f90
program example
use :: iso_c_binding
implicit none

integer(c_int), parameter :: rtld_lazy=1 ! value extracte from the C header file
integer(c_int), parameter :: rtld_now=2 ! value extracte from the C header file
!
! interface to linux API
interface
function dlopen(filename,mode) bind(c,name="dlopen")
! void *dlopen(const char *filename, int mode);
use iso_c_binding
implicit none
type(c_ptr) :: dlopen
character(c_char), intent(in) :: filename(*)
integer(c_int), value :: mode
end function

function dlsym(handle,name) bind(c,name="dlsym")
! void *dlsym(void *handle, const char *name);
use iso_c_binding
implicit none
type(c_funptr) :: dlsym
type(c_ptr), value :: handle
character(c_char), intent(in) :: name(*)
end function

function dlclose(handle) bind(c,name="dlclose")
! int dlclose(void *handle);
use iso_c_binding
implicit none
integer(c_int) :: dlclose
type(c_ptr), value :: handle
end function
end interface

! Define interface of call-back routine.
abstract interface
subroutine called_proc (i, i2) bind(c)
use, intrinsic :: iso_c_binding
integer(c_int), intent(in) :: i
integer(c_int), intent(out) :: i2
end subroutine called_proc
end interface

! testing the dynamic loading
integer i, i2
type(c_funptr) :: proc_addr
type(c_ptr) :: handle
character(256) :: pName, lName

procedure(called_proc), bind(c), pointer :: proc
!
i = 15

handle=dlopen("./test.so"//c_null_char, RTLD_LAZY)
if (.not. c_associated(handle))then
print*, 'Unable to load DLL ./test.so'
stop
end if
!
proc_addr=dlsym(handle, "t_times2"//c_null_char)
if (.not. c_associated(proc_addr))then
write(*,*) 'Unable to load the procedure t_times2'
stop
end if
call c_f_procpointer( proc_addr, proc )
call proc(i,i2)
write(*,*) "t_times2, i2=", i2
!
proc_addr=dlsym( handle, "t_square"//c_null_char )
if ( .not. c_associated(proc_addr) )then
write(*,*)'Unable to load the procedure t_square'
stop
end if
call c_f_procpointer(proc_addr, proc)
call proc(i,i2)
write(*,*) "t_square, i2=", i2
contains
end program example

Compiled and run as:

[username@hostname:~/test]$gfortran -o example example.f90 -ldl
[username@hostname:~/test]$./example
t_times2, i2= 30
t_square, i2= 225
[username@hostname:~/test]$

linking fortran code to library

your example does work, but you are just missing a small thing: When using a shared library, your program (main.f90 / a.out) will try to find the linked library in one of the library folders (such as /lib*, /usr/lib* or /usr/local/lib*).

If you want to specify another folder for your shared library (for example for testing/debugging), you can use the enviroment variable LD_LIBRARY_PATH to "tell" linux another place to look for shared libraries.

So, assuming you wrote your program in the folder /home/mojijoon/fortran you can get the correct output after setting the library path by:

$: export LD_LIBRARY_PATH=LD_LIBRARY_PATH:/home/mojijoon/fortran
$: ./a.out
ok

You can find more information on shared libraries (and the LD_LIBRARY_PATH enviromental variable) here:
tldp.org - shared libraries



Related Topics



Leave a reply



Submit