Prev: removing /save fortran compiler option
Next: Why not 0?
From: Arjen Markus on 5 Jul 2010 04:53 Hello, I am experimenting with dynamically loading a library and its procedures. So far I have not achieved what I want though - I am wrapping the dlopen() and dlsym() system functions on Linux via the iso_c_binding module. I am using Intel Fortran 11.0 on Linux for this, but ultimately this should work on Windows too (there I have completely different problems though). The problem is: - I can load the library - at least I get no error message - I can not load the symbol (write_string_) - dlsym() claims it does not exist - If I use the _same_ shared library in a program that simply calls write_string (the underscore is the decoration added by the compiler), it works fine. I include the code below, as well as my sequence of commands to create the program and library. (It is all a bit lengthy, I apologize). The output from the program on my system is this: In load_library Before system_load_library A B After system_load_library In load_library Before system_load_library A B After system_load_library After get_procedure F Could not load the procedure "write_string_" Error message: test_lib.dll: cannot open shared object file: No such file or directory Error message: (null) Error message: (null) Error message: write_string_: undefined symbol: write_string_ The library that gets loaded has this contents (nm -D test_lib.so): 000019e0 A __bss_start w __cxa_finalize 000018b4 A _DYNAMIC 000019e0 A _edata 000019e4 A _end 00000820 T _fini U for_trim U for_write_seq_lis U for_write_seq_lis_xmit 000019c8 A _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 00000564 T _init w _Jv_RegisterClasses 00000750 T write_string_ 00000660 T write_string_doubled_ As you can see the routine I want is present. So why doesn't dlsym() find it? Regards, Arjen ---- ***The shell script to compile and link it all: #!/bin/sh # # Simple script to build the program # .. /opt/intel/Compiler/11.0/081/bin/ifortvars.sh ia32 gcc -c print_error.c ifort -o test_lib.so test_lib.f90 -shared ifort -c linux_dynlib.f90 ifort -c dynlib.f90 ifort -o test_dynlib test_dynlib.f90 dynlib.o linux_dynlib.o print_error.o ***Here is the source code for the main program: ! test_dynlib.f90 -- ! Test the module for dealing with dynamic libraries ! program test_dynlib use dynamic_libraries implicit none type(dynamic_library) :: dynlib logical :: success procedure(), pointer :: proc ! ! Try the Windows version ! call load_library( dynlib, 'test_lib.dll', success ) if ( .not. success ) then ! ! Try the Linux version ! call load_library( dynlib, 'test_lib.so', success ) endif if ( .not. success ) then write(*,*) 'Could not load the library' else call get_procedure( dynlib, 'write_string_', proc, success ) if ( .not. success ) then write(*,*) 'Could not load the procedure "write_string_"' else call proc( "SUCCESS!" ) endif endif end program test_dynlib ***The module dynamic_libraries is this: ! dynlib.f90 -- ! Module to handle procedures from dynamic libraries ! Requires Fortran 2003 ! ! Note: ! There are two implementations of the basic routines, ! one for Windows and one for Linux/OSX, as the ! underlying system routines have different APIs ! ! TODO: ! Register the contents of an internal library ! and retrieving that ! ! TODO: ! Handle module names ! module dynamic_libraries use iso_c_binding use system_dynamic_libraries implicit none private type dynamic_library !private integer(kind=c_long) :: handle = 0 logical :: loaded = .false. logical :: internal = .false. character(len=512) :: name end type dynamic_library public :: dynamic_library public :: load_library public :: get_procedure !public :: register_internal_library !public :: register_procedure contains ! load_library -- ! Load a dynamic library (DLL/SO) ! ! Arguments: ! dynlib Variable holding the handle to the library ! name Name of the library to load ! success Whether it was loaded successfully or not ! subroutine load_library( dynlib, name, success ) type(dynamic_library), intent(inout) :: dynlib character(len=*), intent(in) :: name logical, intent(out) :: success character(kind=c_char), dimension(512) :: cname integer :: i success = .false. write(*,*) 'In load_library' dynlib%loaded = .false. dynlib%internal = .false. dynlib%name = name cname(1:len(name)+1) = (/ ( name(i:i), i = 1,len(name) ), char(0) /) write(*,*) 'Before system_load_library' call system_load_library( dynlib%handle, cname ) write(*,*) 'After system_load_library' if ( dynlib%handle /= 0_c_long ) then dynlib%loaded = .true. success = .true. endif end subroutine load_library ! get_procedure -- ! Get a procedure pointer from a loaded dynamic library (DLL/SO) ! ! Arguments: ! dynlib Variable holding the handle to the library ! name Name of the procedure to get ! proc Pointer to the procedure ! success Whether it was loaded successfully or not ! subroutine get_procedure( dynlib, name, proc, success ) type(dynamic_library), intent(inout) :: dynlib character(len=*), intent(in) :: name procedure(), pointer :: proc logical, intent(out) :: success character(kind=c_char), dimension(512) :: cname type(c_funptr) :: cproc integer :: i success = .false. proc => null() if ( .not. dynlib%loaded ) then return endif if ( .not. dynlib%internal ) then cname(1:len(name)+1) = (/ ( name(i:i), i = 1,len(name) ), char(0) /) call system_get_procedure( dynlib%handle, cname, cproc, success ) if ( success ) then call c_f_procpointer( cproc, proc ) endif else ! TODO endif end subroutine get_procedure end module dynamic_libraries ***And the Linux/Ifort version of system_dynamic_libraries is: ! linux_dynlib -- ! Implementation for Linux and OS/X of low-level routines ! that deal with dynamic libraries ! module system_dynamic_libraries use iso_c_binding implicit none integer(kind=c_int), parameter :: rtld_lazy = 1 interface function c_load_library( cname, load_type ) bind(c, name='dlopen' ) use iso_c_binding character(kind=c_char), dimension(*) :: cname integer(kind=c_int), value :: load_type integer(kind=c_long) :: c_load_library end function c_load_library end interface interface function c_get_procedure( handle, cname ) bind(c, name='dlsym' ) use iso_c_binding integer(kind=c_long) :: handle character(kind=c_char), dimension(*) :: cname type(c_funptr) :: c_get_procedure end function c_get_procedure end interface interface subroutine c_print_error( ) bind(c, name='print_error' ) use iso_c_binding end subroutine c_print_error end interface contains ! system_load_library -- ! Load the library ! ! Arguments: ! handle Handle to the library ! cname Null-terminated name of the library ! ! Returns: ! Handle to the library ! subroutine system_load_library( handle, cname ) integer(kind=c_long) :: handle character(len=1), dimension(*) :: cname integer(kind=c_int), parameter :: load_type = rtld_lazy write(*,*) 'A' handle = c_load_library( cname, load_type ) call c_print_error write(*,*) 'B' end subroutine system_load_library ! system_get_procedure -- ! Get the procedure ! ! Arguments: ! handle Handle to the library ! cname Null-terminated name of the procedure ! cproc C-style procedure pointer ! success Whether successful or not ! ! Returns: ! Handle to the library ! subroutine system_get_procedure( handle, cname, cproc, success ) integer(kind=c_long), value :: handle character(len=1), dimension(*) :: cname type(c_funptr) :: cproc logical :: success call c_print_error cproc = c_get_procedure( handle, cname ) call c_print_error success = transfer(cproc, 0_c_long) /= 0_c_long write(*,*) 'After get_procedure', success end subroutine system_get_procedure end module system_dynamic_libraries ***To print the error from dlopen()/dlsym(): #include <stdlib.h> #include <stdio.h> #include <dlfcn.h> void print_error( void ) { printf("Error message: %s\n", dlerror()); } ***The source for the library is almost trivial: ! test_lib.f90 -- ! Provide a simple dynamic library for testing ! subroutine write_string( string ) !dec$ attributes dllexport :: write_string implicit none character(len=*) :: string write(*,*) 'Write_string: ',trim(string) end subroutine write_string subroutine write_string_doubled( string ) !dec$ attributes dllexport :: write_string_doubled implicit none character(len=*) :: string write(*,*) 'Write_string twice: ', trim(string), ' -- ', trim(string) end subroutine write_string_doubled
From: Simon on 5 Jul 2010 05:31 On 05/07/2010 09:53, Arjen Markus wrote: > Hello, > > I am experimenting with dynamically loading a library and its > procedures. So far I have > not achieved what I want though - I am wrapping the dlopen() and > dlsym() system functions > on Linux via the iso_c_binding module. > > I am using Intel Fortran 11.0 on Linux for this, but ultimately this > should > work on Windows too (there I have completely different problems > though). > > The problem is: > - I can load the library - at least I get no error message > - I can not load the symbol (write_string_) - dlsym() claims it does > not exist > - If I use the _same_ shared library in a program that simply calls > write_string (the underscore > is the decoration added by the compiler), it works fine. > <snip> I'll try to find time later today to look at this, in the meantime have you tried building the shared library using ld with --export-dynamic to ensure all the symbols are available? Simon
From: Arjen Markus on 5 Jul 2010 05:35 On 5 jul, 11:31, Simon <si...(a)whiteowl.co.uk> wrote: > On 05/07/2010 09:53, Arjen Markus wrote: > > > > > Hello, > > > I am experimenting with dynamically loading a library and its > > procedures. So far I have > > not achieved what I want though - I am wrapping the dlopen() and > > dlsym() system functions > > on Linux via the iso_c_binding module. > > > I am using Intel Fortran 11.0 on Linux for this, but ultimately this > > should > > work on Windows too (there I have completely different problems > > though). > > > The problem is: > > - I can load the library - at least I get no error message > > - I can not load the symbol (write_string_) - dlsym() claims it does > > not exist > > - If I use the _same_ shared library in a program that simply calls > > write_string (the underscore > > is the decoration added by the compiler), it works fine. > > <snip> > > I'll try to find time later today to look at this, in the meantime have > you tried building the shared library using ld with --export-dynamic to > ensure all the symbols are available? > > Simon- Tekst uit oorspronkelijk bericht niet weergeven - > > - Tekst uit oorspronkelijk bericht weergeven - Hm, I did not. I will try that. Regards, Arjen
From: Arjen Markus on 5 Jul 2010 06:05 On 5 jul, 11:31, Simon <si...(a)whiteowl.co.uk> wrote: > On 05/07/2010 09:53, Arjen Markus wrote: > > > > > Hello, > > > I am experimenting with dynamically loading a library and its > > procedures. So far I have > > not achieved what I want though - I am wrapping the dlopen() and > > dlsym() system functions > > on Linux via the iso_c_binding module. > > > I am using Intel Fortran 11.0 on Linux for this, but ultimately this > > should > > work on Windows too (there I have completely different problems > > though). > > > The problem is: > > - I can load the library - at least I get no error message > > - I can not load the symbol (write_string_) - dlsym() claims it does > > not exist > > - If I use the _same_ shared library in a program that simply calls > > write_string (the underscore > > is the decoration added by the compiler), it works fine. > > <snip> > > I'll try to find time later today to look at this, in the meantime have > you tried building the shared library using ld with --export-dynamic to > ensure all the symbols are available? > > Simon- Tekst uit oorspronkelijk bericht niet weergeven - > > - Tekst uit oorspronkelijk bericht weergeven - Hi Simon, no need to check it - I found the problem: interface function c_get_procedure( handle, cname ) bind(c, name='dlsym' ) use iso_c_binding integer(kind=c_long) :: handle character(kind=c_char), dimension(*) :: cname type(c_funptr) :: c_get_procedure end function c_get_procedure end interface should be: interface function c_get_procedure( handle, cname ) bind(c, name='dlsym' ) use iso_c_binding integer(kind=c_long), value :: handle <== value attribute was missing character(kind=c_char), dimension(*) :: cname type(c_funptr) :: c_get_procedure end function c_get_procedure end interface With this change it is working fine. At least on Linux. (I am still getting used to iso_c_binding ;)) Regards, Arjen
From: Arjen Markus on 5 Jul 2010 07:06
On 5 jul, 10:53, Arjen Markus <arjen.markus...(a)gmail.com> wrote: > I am using Intel Fortran 11.0 on Linux for this, but ultimately this > should > work on Windows too (there I have completely different problems > though). > On Windows I get very different problems indeed. The one I want to tackle first is the problem with gfortran that I see: win_dynlib.o:win_dynlib.f90:(.text+0x14a): undefined reference to `LoadLibraryW' collect2: ld returned 1 exit status where the source for win_dynlib.f90 is given below. At the command-line I specified --enable-stdcall-fixup to suppress a warning about GetProcAddress@8 - but the same linking procedure that is behind that fixup fails with LoadLibrary(A or W). Any thoughts? Regards, Arjen ---- ! win_dynlib -- ! Implementation for GNU Fortran on Windows of low-level routines ! that deal with dynamic libraries ! module system_dynamic_libraries use iso_c_binding implicit none interface function c_load_library( cname ) bind(c, name='LoadLibraryW') use iso_c_binding character(kind=c_char), dimension(*) :: cname integer(kind=c_long) :: c_load_library end function c_load_library end interface interface function c_get_procedure( handle, cname ) bind(c, name='GetProcAddress') use iso_c_binding integer(kind=c_long), value :: handle character(kind=c_char), dimension(*) :: cname type(c_funptr) :: c_get_procedure end function c_get_procedure end interface contains ! system_load_library -- ! Load the library ! ! Arguments: ! handle Handle to the library ! cname Null-terminated name of the library ! ! Returns: ! Handle to the library ! subroutine system_load_library( handle, cname ) integer(kind=c_long) :: handle character(len=1), dimension(*) :: cname write(*,*) ' in system_load_library', cname(1:20), ichar(cname(13)) handle = c_load_library( cname ) write(*,*) ' system_load_library: loaded' end subroutine system_load_library ! system_get_procedure -- ! Get the procedure ! ! Arguments: ! handle Handle to the library ! cname Null-terminated name of the procedure ! cproc C-style procedure pointer ! success Whether successful or not ! ! Returns: ! Handle to the library ! subroutine system_get_procedure( handle, cname, cproc, success ) integer(kind=c_long) :: handle character(len=1), dimension(*) :: cname type(c_funptr) :: cproc logical :: success cproc = c_get_procedure( handle, cname ) success = transfer(cproc, 0_c_long) /= 0_c_long end subroutine system_get_procedure end module system_dynamic_libraries |