From: Arjen Markus on
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
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
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
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
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
 |  Next  |  Last
Pages: 1 2 3 4
Prev: removing /save fortran compiler option
Next: Why not 0?