Background: I am in a situation where I have to make use of an old Fortran95 library in a new C++ project. The F95 library is extensive, has tons of small modules, poorly documented, and it was mostly auto-generated about a decade ago by some obscure computer algebra system (by people on a different continent). So basically heirloom code, but it works and it is currently irreplaceable.
Luckily I have the source code and it can be compiled with current versions of ifort, but I am not too familiar with Fortran, and would rather not touch the old code in any significant way.
So suppose I have this Fortran code (pes_shell.f90):
subroutine pes_init()
use pes,wp=>pes_wp
implicit none
real,parameter::auang=0.5291772083
call pes0_init (dir='coef')
call pes1_init (pes_x3y1z1u1_sysall)
return
end subroutine pes_init
The functions pes0_init(...) and pes1_init(...) lead into the abyssal depths of the Fortran library and they are contained in the pes module.
I can compile this to an object file, if I give ifort the path for the modules:
ifort -r8 -O2 -c pes_shell.f90 -I/home/debianuser/PES/PES_library/lib/mod
My POC C++ code, calling pes_init():
extern "C"{
void pes_init_();
}
int main(){
pes_init_();
return 0;
}
This can also be compiled to an object file, with icpc:
icpc -c PEStest.cpp
However I cannot figure out how to link the two object files, plus the truckload of fortran modules, into a final executable.
I have tried just simply using icpc, but it cannot seem to be able to find the Fortran functions, even if I give it the location of the module files:
icpc -I/home/debianuser/PES/PES_library/lib/mod -o test.x pes_shell.o PEStest.o
pes_shell.o: In function `pes_shell_mp_f_':
pes_shell.f90:(.text+0x595): undefined reference to `pes_x3y1z1u1_mp_pes_x3y1z1u1_pot_'
pes_shell.o: In function `pes_shell_mp_pes_init_':
pes_shell.f90:(.text+0x5f0): undefined reference to `pes0_mp_pes0_init_'
pes_shell.f90:(.text+0x603): undefined reference to `pes1c_mp_pes1_init_'
PEStest.o: In function `main':
PEStest.cpp:(.text+0x2b): undefined reference to `pes_init_'
EDIT:
Pointing the linker to the directory where libpes.a can be found eliminates the problem of locating the function that was referenced in the c++ code, but icpc still cannot find the fortran functions that are being called from fortran codes:
icpc -I/home/debianuser/PES/PES_library/lib/mod -L/home/debianuser/PES/PES_library/lib/pes-xyz -lpes -o test.x PEStest.o pes_shell_new.o
pes_shell_new.o: In function `f_':
pes_shell_new.f90:(.text+0x585): undefined reference to `pes_x3y1z1u1_mp_pes_x3y1z1u1_pot_'
pes_shell_new.o: In function `pes_init_':
pes_shell_new.f90:(.text+0x5e0): undefined reference to `pes0_mp_pes0_init_'
pes_shell_new.f90:(.text+0x5f3): undefined reference to `pes1c_mp_pes1_init_'
Related
I am using gfortran compiler and ld linker in LINUX ubuntu 16.04.
When I am compiling some program written in Fortran 77, I have the following error message:
libdist.a(setup.F.o): In function `setup_':
setup.F:(.text+0x26c4): undefined reference to `mpi_send_'
setup.F:(.text+0x2b3c): undefined reference to `mpi_recv_'
setup.F:(.text+0x7984): undefined reference to `mpi_send_'
setup.F:(.text+0xb107): undefined reference to `mpi_recv_'
I guess it is about the position of error. However, it is difficult to me the find where is the error.
Can I have a better presentation of the position of error? Such as the c/cxx error: "setup.F:15:12: "
It is an address. And is is NOT a Fortran error. It is a linker error. You can get the very same error from a C or C++ code or any other compiled code linked by the same linker.
Generate debugging symbols (compiler option -g or -ggdb or similar - consult Debugging options in the GCC Fortran manual) to get something more meaningful. But if you do that you will realize that it only points you to some location where you do:
call mpi_send(...)
in some subroutine or function called setup().
So, it is not terribly useful in this case. The important thing is that you should link the appropriate MPI library. Normally that is done by calling mpif90 or mpifort or a similar wrapper which is called instead of gfortran. Consult the documentation of your MPI library implementation.
This question already has answers here:
How to change entry point of C program with gcc?
(4 answers)
Closed 5 years ago.
I am trying to compile a function (not called main) that can be integrated in another code or directly executed (after linking).
I try it one my mac, and work well.
I finally test it on Linux (CentOS and ubuntu). However, the task looks harder as expected on Linux.
The source code is the following one (just to explain the problem)
test.cpp:
#include <cstdio>
#ifdef __cplusplus
extern "C" {
#endif
int test(int argc, char const *argv[]);
#ifdef __cplusplus
}
#endif
int test(int argc, char const *argv[]) {
fprintf(stderr, "%s\n", "test");
return 0;
}
Compilation line on MacOS
g++ -c test.cpp -o test.o && g++ test.o -o test -e _test
and on Linux
g++ -c test.cpp -o test.o && g++ test.o -o test -e test
I try on my MacOS with clang, g++ and Intel compiler, all 3 works fine.
And I try with g++ and the Intel compiler on Linux, always, the same error.
usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
Any advice, explanation or solution, on what I am doing wrong or missing would be very helpful.
Thanks
Edit:
Currently, I have a "define" to create a main, but if we have lots of function we are obligated to do two compilations each time (one for the function version and one for the execution) and make finally the code heavier.
Like discussed in this topic is there a GCC compiler/linker option to change the name of main?
To don't do a XY I inherited from a bunch of small programs that I want to put to gather, that it is easier to use (for remote execution ...). However, each one need to be able to be executed independently if needed, for debugging,... I hesitate, between using "execv" and just convert each main as a function. I probably take the bad chose.
Edit:
The final goal is to be able to have independent programs. But that we can call from an external software too.
Solution:
The solution looks to be, to a call to the main through a dlopen
You cannot do that (and even if it appears to work on MacOSX it is implementation specific and undefined behavior).
Linux crt0 is doing more complex stuff that what you think.
The C standard (e.g. n1570 for C11) requires a main function for hosted implementations (ยง5.1.2.2.1) :
The function called at program startup is named main. The implementation declares no prototype for this function.
And the C++ standard also requires a main and strongly requires some processing (e.g. construction of static data) to be done before main is running and after it has returned (and various crt0 tricks are implementing that feature on Linux).
If you want to understand gory details (and they are not easy!), study the ABI and the (free software) source code of the implementation of the crt0.
I am trying to compile a function (not called main) that can be integrated in another code
BTW, to use dynamically some code (e.g. plug-ins) from another program, consider using the dynamic linker. I recommend using the POSIX compliant dlopen(3) with dlsym(3) on position-independent code shared libraries. It works on most Unix flavors (including MacOSX & Linux & Solaris & AIX). For C++ code beware of name mangling so read at least the C++ dlopen mini howto.
Read also the Program Library HowTo.
Problems with libraries, they cannot be executed, no ?
I don't understand what that means. You certainly can load a plugin then run code inside it from the main program dlopen-ing it.
(and on Linux, some libraries like libc.so are even specially built to also work as an executable; I don't recommend this practice for your own code)
You might take several days to read Drepper's How To Write Shared Libraries (but it is advanced stuff).
If you want to add some code at runtime, read also this answer and that one.
The final goal is to be able to have independent program. But that we can call from an external software too
You can't do that (and it would make no sense). However, you could have conventions for communicating with other running programs (i.e. processes), using inter-process communication such as pipe(7)-s and many others. Read Advanced Linux Programming first and before coding. Read also Operating Systems : Three Easy Pieces
The solution looks to be, to a call to the main through a dlopen
Calling the main function via dlopen & dlsym is forbidden by the C++ standard (which disallows using a pointer to main). The main function has a very specific status and role (and is compiled specially; the compiler knows about main).
(perhaps calling main obtained by dlsym would appear to work on some Linux systems, but it certainly is undefined behavior so you should not do that)
Update 1
According to this SO post, Free Pascal may not link against C++ object file, which means static C++ libraries, when libstdc++.so.6.X is used as the C++ lib.
It should be noted that for that specific hello-world program in that SO post, Free Pascal can link against C++ object file using libstdc++.so.5 as the C++ lib. Note libstdc++.so.5 is with GCC 3.3.X.
Update 2
According to the Free Pascal future plans, Linking with C++ code is not until next major version is finished.
Update 3
According to "How to use C code in Free Pascal projects", Free Pascal cannot link in C++ objects directly. They should be placed into shared library to be used from Free Pasacal.
The source code of the indigo cheminformatics package can be compiled into static libraries and provides a plain-C API interface to its C++ core.
The procedure is shown:
cd indigo
module add gcc/4.7.2 # or gcc/4.3.2
module add cmake/2.8.8
python ./build_scripts/indigo-release-libs.py --preset=linux64
Part of the tree structure is shown:
indigo
./dist/indigo-libs-1.1.12-linux64-static.zip
./dist/LICENSE.GPL
./dist/indigo-inchi.h
./dist/indigo-renderer.h
./dist/indigo.h
./dist/static
./dist/static/Linux
./dist/static/Linux/x64
./dist/static/Linux/x64/libcairo.a
./dist/static/Linux/x64/libcommon.a
./dist/static/Linux/x64/libgraph.a
./dist/static/Linux/x64/libinchi.a
./dist/static/Linux/x64/libindigo-inchi-static.a
./dist/static/Linux/x64/libindigo-renderer-static.a
./dist/static/Linux/x64/libindigo-static.a
./dist/static/Linux/x64/liblayout.a
./dist/static/Linux/x64/libmolecule.a
./dist/static/Linux/x64/libpixman.a
./dist/static/Linux/x64/libpng.a
./dist/static/Linux/x64/libreaction.a
./dist/static/Linux/x64/librender2d.a
./dist/static/Linux/x64/libtinyxml.a
./dist/static/Linux/x64/libz.a
To compile .c and static link against the static indigo libraries:
Source code of a sample cTest.c file is shown:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "indigo.h"
int main (void) { printf("%s\n", indigoVersion()); }
, the procedure is shown:
cd indigo/dist/static/Linux/x64
module add gcc/4.7.2 # or gcc/4.3.2
g++ -I../../../ cTest.c -o cTest libindigo-static.a libreaction.a liblayout.a libmolecule.a libgraph.a libcommon.a libz.a libtinyxml.a -lpthread
To compile .pp and static link against the static indigo libraries:
Source code of a sample fpcTest.pp file is shown:
program fpcTest;
{$MODE DELPHI}
{$APPTYPE CONSOLE}
uses
SysUtils;
// {$linklib stdc++}
{$link /usr/lib64/libstdc++.so.6}
{$linklib pthread}
{$linklib libindigo-static}
{$linklib libreaction}
{$linklib liblayout}
{$linklib libmolecule}
{$linklib libgraph}
{$linklib libcommon}
{$linklib libz}
{$linklib libtinyxml}
function indigoVersion (): PChar; cdecl; external 'libindigo-static';
begin
WriteLn(indigoVersion);
end.
, the procedure is shown:
cd indigo/dist/static/Linux/x64
~/fpc-2.6.4/bin/fpc fpc_static.pp
However, it complains "undefined reference to __dso_handle". The Error msg is shown below:
Free Pascal Compiler version 2.6.4 [2014/03/03] for x86_64
Copyright (c) 1993-2014 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling fpc_static.pp
fpc_static.pp(4,2) Note: APPTYPE is not supported by the target OS
Linking fpc_static
/usr/bin/ld: warning: link.res contains output sections; did you forget -T?
./libindigo-static.a(option_manager.cpp.o): In function `_GLOBAL__sub_I_option_manager.cpp':
option_manager.cpp:(.text.startup+0x3): undefined reference to `__dso_handle'
./libindigo-static.a(indigo.cpp.o): In function `Indigo::init()':
indigo.cpp:(.text+0x3bf): undefined reference to `__dso_handle'
./libindigo-static.a(indigo.cpp.o): In function `_GLOBAL__sub_I_indigo.cpp':
indigo.cpp:(.text.startup+0x2e): undefined reference to `__dso_handle'
indigo.cpp:(.text.startup+0x5b): undefined reference to `__dso_handle'
./libcommon.a(profiling.cpp.o): In function `_GLOBAL__sub_I_profiling.cpp':
profiling.cpp:(.text.startup+0x22): undefined reference to `__dso_handle'
./libcommon.a(profiling.cpp.o):profiling.cpp:(.text.startup+0x40): more undefined references to `__dso_handle' follow
fpc_static.pp(25,1) Error: Error while linking
fpc_static.pp(25,1) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: ~/fpc-2.6.4/bin/ppcx64 returned an error exitcode (normal if you did not specify a source file to be compiled)
Could you help to comment about the reason and the workaround ?
I think in your references you are majorly confusing "pascal calling C++ classes" and "pascal and C++ libraries linked into the same project". update 1 is about the latter, update 2 is about the former, update 3 is old and not really related I guess.
My guess is that the modified FPC startup code that initializes libraries via CTOR and DTOR, ONLY initializes shared libraries, and not static libraries. IOW any C/C++ state linked into the main binary might not be initialized.
It will probably require an exact understanding of CTOR and DTOR internal working, and specially in combination with static linking to figure this out.
To make it clear: the "C++" support in trunk (on which the next FPC version will be based) is about calling (gcc) C++ classes directly. IOW making FPC understand gcc mangling (in some gcc version, since C++ mangling is usually version dependent long term). And while it is in the branch, it is not known yet if it will be considered finished and stable in the next fixes branch.
I am working with two sets of FORTRAN code, for example, codeA.f and codeB.f. In each set of code there are subroutines with the same name, but which perform significantly different tasks (as well as common blocks).
codeA.f:
subroutine init
c do something here ...
end
codeB.f:
subroutine init
c do something else here ...
end
I create two C++ wrappers for both routines (wrapperB is identical, but swap B for A).
wrapperA.cpp:
namespace codeA {
extern "C"{void init_();}
void init() {init_();}
}
wrapperA.h:
namespace codeA {
init();
}
I then compile the two wrappers into shared libraries.
Makefile:
codeA.o : codeA.F
g77 -fno-automatic -fPIC -O3 -c codeA.f -o codeA.o
wrapperA.o : wrapperA.cpp
g++ -fPIC -c wrapperA.cpp -o wrapperA.o
libwrapperA.so : codeA.o wrapperA.o
g++ -fPIC -shared -lg2c codeA.o wrapperA.o -o libwrapperA.so
Finally, I want to be able to call the two different subroutines from the same program.
main.cpp:
#include "wrapperA.h"
#include "wrapperB.h"
main {
wrapperA::init();
wrapperB::init();
}
The main program is linked against the two shared wrapper libraries.
Makefile:
main : main.cpp
g++ main.cpp -I. -L. -lwrapperA -lwrapperB -o main
Everything compiles and there are no complaints. However, when the program "main" is run, the init() commands perform the same action, dictated by which of the wrapper libraries was linked against first. However, if I link against only one of the libraries, the linker complains about missing symbols.
I have two question about this:
a) Is it possible to link against two different FORTRAN routines using only namespaces or some similar method to what I am doing? If so, what am I doing wrong?
b) If a) is not possible, is it possible to resolve the conflicts by instructing the compiler to append the symbols with some unique identifier when creating the FORTRAN objects (I am using gcc version 4.1.2 20080704 (Red Hat 4.1.2-51))?
Thanks for any insight into this.
Your compile commands show the use of g77, which is no longer maintained and also implies the use of a very old version of Fortran (i.e., FORTRAN 77). You are also using an old version of gcc. If you use gcc and gfortran of version 4.3 or higher -- preferably 4.4 or higher -- then you can use the Fortran 2003 ISO C Binding to solve your problem. In the Fortran code you can assign externally visible names to the routines with the "bind" option of the ISO C Binding. Just assign different "bind" names to the two routines. Then they won't clash at the library & linker level:
In one Fortran file use
subroutine init (stuff...) bind (C, name="init_V1")
and the other file
subroutine init (stuff...) bind (C, name="init_V2")
and the routines will be visible to C, C++, the linker, etc. as init_V1 and init_V2 without clashing.
Have a look at this SO question, which is very similar to your problem : Static and shared library symbol conflicts?
Basically, you have two shared libraries defining different symbols : wrapperA::init() and wrapperB::init() ; but also conflicting symbols : _init__(). So when the linker resolves, it use the first of the conflicting symbol it find.
If you remove the unneeded external visibility of _init__() in your libraries by using the techniques given in the SO question : http://www.gnu.org/software/gnulib/manual/html_node/Exported-Symbols-of-Shared-Libraries.html, then I think you will have solved your problem. Each wrapper::init() will then call internally it's proper _init__() without any conflict.
I'm a novice at C++. Be patient if this is incoherent. I'm called upon to build a large system on linux that was originally built on OS X, where it works fine. The original authors are no longer with the company. The build system makes use of autotools, but there are also some hand made Makefiles which walk through the system calling the auto-made Makefiles. I've managed to get all of the c++ code compiled. The build system also uses libtools, and shared libraries are produced and deposited in /usr/local/lib.
So now I'd like to use these libraries. I've written a short program that simply instantiates an object of class ds_dictionary and calls one of its methods. Here it is:
#include <iostream>
#include <DSUtils/DSUtils.h>
int main (int argc, char * const argv[]) {
int32_t integer_data=123;
char key_alice_integer[] = "alice_integer";
ds_dictionary my_dict;
my_dict.add_int(key_alice_integer, integer_data);
return 0;
}
I compile this with
g++ -lDSUtils -o main my_test_code.cpp
With the result:
//usr/local/lib/libDSUtils.so: undefined reference to `ds_breakdown_from_time_interval'
//usr/local/lib/libDSUtils.so: undefined reference to `ds_date_breakdown_with_string'
//usr/local/lib/libDSUtils.so: undefined reference to `ds_seconds_duration_of_interval'
... (about 25 lines like these)
collect2: ld returned 1 exit status
Let's look inside the library:
garyp#VM:/usr/local/lib$ nm libDSUtils.so | grep ds_breakdown_from_time
U ds_breakdown_from_time_interval
The "U" in the line above ... does that mean that the library wasn't built correctly?
Am I calling g++ correctly?
Do I have to put something in the code to tell it that I'm using functions found in that library?
What are possible errors? Where should I start poking around?
EDIT:
Aha. The library DSUtils is built from several c++ sources. There is one c program in the source, and it contains all of the problem functions. The Makefile system doesn't deal at all with that one c file. That c program compiles. Ideally I suppose I'd figure out how to modify the Makefile to compile that file and add it to the library, but I'm not to the point where I can figure out how to do that.
Can I add the .o file to the existing library? How? Create a library with one file? etc?
EDIT_2: I simply did
g++ -o main -lDSUtils main.o my_new_objectfile.o
and the thing compiles, links, and runs without error. Should that work? After fixing a logic bug, it does work.
This
U ds_breakdown_from_time_interval
tells me that ds_breakdown_from_time_interval will be resolved by another library during runtime. So I am guessing you need to link to the library that defines ds_breakdown_from_time_interval.