将函数指针从 Fortran 传递到 C,与 c_f_procpointer 相反

Passing function pointers from Fortran to C, reverse of c_f_procpointer

我正在尝试使用基于 this 的 Fortran 在 Linux 中加载动态库,但我想添加对 dladdr 的支持。基本上我的代码是:

procedure(proto), pointer :: my_func
type(c_funptr) :: funptr

funptr = dlsym(handle,'myfunction')
if (c_associated(funptr)) then
  call c_f_procpointer(funptr, my_func)
else
  my_func => myfunction
end if

write(6,*) dladdr(???,info)

缺少许多细节,但重要的部分是我想传递给 dladdrmy_func 表示的实际函数,它是指向其他函数的过程指针: dlsym 或明确的 Fortran 关联。所以我的问题是我不知道在 ???.

中放什么

如果我使用 funptrc_funloc(myfunction),我会成功(非零 return 值),所以至少 dladdr 调用有效。但是我不能使用 my_func(gfortran 编译器抱怨错误的参数类型),并且 c_loc(my_func)c_funloc(my_func) 都失败(零 return 值)。我认为问题是我必须"dereference" my_func一次,然后得到那个C函数指针。我该怎么做?

完整代码:

dlfcn.f90

MODULE DLFCN
   USE ISO_C_BINDING
   IMPLICIT NONE
   PRIVATE
   PUBLIC :: DLOpen, DLSym, DLClose, DLError, DLAddr ! DL API

   ! Valid modes for mode in DLOpen:
   INTEGER, PARAMETER, PUBLIC :: RTLD_LAZY=1, RTLD_NOW=2, RTLD_GLOBAL=256, RTLD_LOCAL=0
      ! Obtained from the output of the previously listed C program

   INTERFACE ! All we need is interfaces for the prototypes in <dlfcn.h>
      FUNCTION DLOpen(file,mode) RESULT(handle) BIND(C,NAME="dlopen")
         ! void *dlopen(const char *file, int mode);
         USE ISO_C_BINDING
         CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: file
            ! C strings should be declared as character arrays
         INTEGER(C_INT), VALUE :: mode
         TYPE(C_PTR) :: handle
      END FUNCTION
      FUNCTION DLSym(handle,name) RESULT(funptr) BIND(C,NAME="dlsym")
         ! void *dlsym(void *handle, const char *name);
         USE ISO_C_BINDING
         TYPE(C_PTR), VALUE :: handle
         CHARACTER(C_CHAR), DIMENSION(*), INTENT(IN) :: name
         TYPE(C_FUNPTR) :: funptr ! A function pointer
      END FUNCTION
      FUNCTION DLClose(handle) RESULT(status) BIND(C,NAME="dlclose")
         ! int dlclose(void *handle);
         USE ISO_C_BINDING
         TYPE(C_PTR), VALUE :: handle
         INTEGER(C_INT) :: status
      END FUNCTION
      FUNCTION DLError() RESULT(error) BIND(C,NAME="dlerror")
         ! char *dlerror(void);
         USE ISO_C_BINDING
         TYPE(C_PTR) :: error
      END FUNCTION
      ! dladdr is a Glibc extension, not POSIX
      FUNCTION DLAddr(funptr,info) RESULT(output) BIND(C,NAME="dladdr")
         ! int dladdr(void *addr, Dl_info *info)
         USE ISO_C_BINDING
         TYPE(C_FUNPTR) :: funptr ! A function pointer
         TYPE(C_PTR) :: info
         INTEGER(C_INT) :: output
      END FUNCTION
   END INTERFACE

END MODULE

test.f90

program test
use iso_c_binding
use dlfcn
implicit none
abstract interface
  function proto(s,i)
    character(len=*) :: s
    integer :: i
    integer :: proto
  end function proto
end interface

procedure(proto), pointer :: my_func
character(kind=c_char,len=1024) :: libname,funname
type(c_ptr) :: handle=c_null_ptr
type(c_funptr) :: funptr=c_null_funptr
type(c_ptr) :: info

libname = 'libblas.so'
funname = 'xerbla_'
handle = dlopen(trim(libname)//c_null_char, int(ior(rtld_global,rtld_lazy),kind=c_int))
if (c_associated(handle)) then
  write (6,*) trim(libname),' loaded'
else
  write(6,*) 'error loading '//trim(libname)
end if
funptr = dlsym(handle,trim(funname)//c_null_char)
if (c_associated(funptr)) then
  call c_f_procpointer(funptr, my_func)
else
  my_func => myfunction
end if

write(6,*) dladdr(funptr,info)
write(6,*) dladdr(c_funloc(myfunction),info)
write(6,*) dladdr(c_funloc(my_func),info)
!write(6,*) dladdr(my_func,info)

contains

function myfunction(srname,info)
  character(len=*) :: srname
  integer :: info
  integer :: myfunction
  myfunction = 0
end function myfunction

end program test

编译:

gfortran dlfcn.f90 test.f90 -ldl -o test

运行:

$ ./test 
 libblas.so loaded
           1
           1
           0

如果我启用注释掉的行,编译器会说:

write(6,*) dladdr(my_func,info)
                  1
Error: Invalid procedure argument at (1)

这是有道理的,因为 my_func 是一个 Fortran 过程指针,而不是 dladdr 期望的 C 指针。

Fortran 源代码中 DLAddr 的接口与相应 C 函数的记录接口不匹配。两个参数都需要 VALUE 属性。

还值得注意的是 C_FUNLOC 的参数(这是您在 ??? 中需要的)需要是一个可互操作的过程,即具有 BIND 属性的过程。 Fortran 代码中过程指针声明中使用的接口中缺少此属性(如果您希望它具有相同的特性,my_function 中也缺少此属性)。