Do you have to do something special to export a subroutine as a symbol in the library created by fpm?
For example, just running fpm build
on the scaffolded project from fpm new test_lib_symbol
and looking at the symbols in libtest_lib_symbol.a
in the build directory with:
nm -gD build/gfortran_xxxxxxxxx/test_lib_symbol/test_lib_symbol.a
produces no symbols for the customary say_hello
subroutine.
I did also set
library = true
in fpm.toml.
The scaffold project puts the subroutine in a module. Module procedure’s do not have predictable symbols in the produced libraries/object files (or at least their names are compiler specific). If you want to make use of procedure in a module, you should use
that module to make the name available. This is best practice in modern Fortran. I.e.
module example_mod
contains
subroutine example_sub
print *, "Hello, World!"
end subroutine
end module
program example
use example_mod, only: example_sub
call example_sub
end program
Thanks @everythingfunctional .
The reason I was looking for the symbol in the generated (static) library, which I converted to a shared library is because I would like to call the subroutine (e.g., your example_sub) from another programming language.
However, I kept getting symbol not defined error so I checked for it with nm -gD library_name
. and objdump -TC library_name
. Both showed no symbols defined.
So, I was wondering if fpm
was not generating the library correctly or maybe I am failing to do something on my end.
In that case, use the C-interoperability features of the language and give the procedure the bind(C)
attribute. I.e.
module example_mod
contains
subroutine example_sub() bind(C, name="example_sub")
print *, "Hello from Fortran!"
end subroutine
end module
void example_sub();
int main(int argv, char* argc) {
example_sub();
return 0;
}
This works perfectly standalone, i.e., if I take your code and create a shared library from it but when I use fpm to generate a library because I use other dependencies like odepack, etc, the final generated library through fpm does not work.
Do you have a specific code example and error message you can share?
This can be done from any project.
- Generate any new project with fpm new A.
- Set library = true in fpm.toml so that it builds a library.
- Then fpm build or fpm run.
- Search for the generated library.
- Then nm -gD library_name or objdump -TC library_name. You could also convert the generated static library (*.a) to a shared one (.so) first with
gfortran -shared -fPIC -o new_shared_library.so -lgenerated_static_library
We are looking for the say_hello subroutine in the symbol list, which comes with the scaffolded project. If it is there, it can be called from another language but if it is not, then you will always get symbol not found or unable to find entry point when you compile and run from the other language.
Correct, but it seems to work fine for me, so I’m not sure what you’re doing differently. I.e.
$ fpm new --lib callable_library
+ mkdir -p callable_library
+ cd callable_library
+ mkdir -p callable_library/src
+ git config --get user.name > /tmp/file1fm3vp
+ git config --get user.email > /tmp/fileugTyij
+ git config --get user.name > /tmp/fileupi3He
+ git init callable_library
Initialized empty Git repository in /home/brad/scratch/callable_library/.git/
$ cd callable_library
$ vim src/callable_library.f90
$ cat src/callable_library.f90
module callable_library
implicit none
private
public :: say_hello
contains
subroutine say_hello() bind(c)
print *, "Hello, callable_library!"
end subroutine say_hello
end module callable_library
$ fpm build
callable_library.f90 done.
libcallable_library.a done.
[100%] Project compiled successfully.
$ nm `find build -name libcallable_library.a`
src_callable_library.f90.o:
U _gfortran_st_write
U _gfortran_st_write_done
U _gfortran_transfer_character_write
0000000000000000 T say_hello
U __stack_chk_fail
As I mentioned upthread, note that I added the bind(c)
attribute to the subroutine. You must use the C interoperability features to make it valid to call Fortran procedures from other languages.
It is the bind clause that does the trick. I think in this case it is just a compiler convention that the the external symbol is “say_hello” in lower case. If the fortran code had been written as “SAY_HELLO” or “Say_Hello” or any other case combination, the same external symbol would have been generated with that compiler. Another compiler might generate the upper case symbol, or it might use some other mixed-case convention. To fully specify the external name and to be independent of compiler, loader, and OS conventions, the name='say_hello'
argument should be added to the bind clause. [edit: this paragraph is incorrect, there is a defined convention. See below.]
Having said all that, i must admit that I am often faced with several choices for how interfaces are defined and/or specified when using C interop features. Some of this is because the C language itself treats array references and pointer references equivalently, so there is nothing that fortran can do to eliminate that aspect. Is there a good online reference, or even a tutorial, on how best to use the C interop features of fortran?
The standard actually says, in Sec. 18.10.2 Binding labels for procedures,
If a procedure has the BIND attribute with no NAME= specifier, and the procedure is not a dummy procedure, internal procedure, or procedure pointer, then the binding label of the procedure is the same as the name of the procedure using lower case letters.
In the old days yes, it was compiler specific what the symbol for an external procedure was. Some did all lower case, some did all upper case, some appended a single _
, some didn’t, etc. With the bind
attribute, the standard says what it is by default, and the user can dictate if they want something different.
I’ve generally pieced it together as I’ve gone. I think I’ve seen one or two people say they were putting something together, but I don’t remember if or where those got posted. If there’s enough interest from NERSC users I could do a training session on the topic.
1 Like
Thanks @everythingfunctional and @FedericoPerini . It works now following your examples.
The only problem I have now is on the non-Fortran side with “undefined symbol: _gfortran_cfi_desc_to_gfc_desc” but that is probably due to allocations on the Fortran side. So, I have to finally learn FFI with C descriptors.
@everythingfunctional: If you do a training session, could you make it a hybrid session so I and others could join online.
You may also need a -lgfortran
flag if you’re not using gfortran for linking the executable.
NERSC training sessions are usually virtual these days, but are typically restricted to DOE computing facility users for attendance. That said, they’re pretty much always recorded and posted publicly on NERSC’s YouTube channel.
1 Like