I have a question about mixed-language programming (C/C++ and FORTRAN)
using gcc and gfortran. I've searched plenty of "mixing fortran with
language X" and haven't been able to resolve this.
I'm not sure if this is a linking problem or a compiler problem, or both.
I've created three files and I'm using GNU Autotools to build the
crude application, but should be able to build the app from command
line independently.
The C File (main.c) will be the driving app, that calls several
FORTRAN functions:
/* this is a simple program */
#include <stdio.h>
/* add the extern function definition */
#include "fooonly.h"
// this is not working for the mixed language programming stuff yet...
/* this is the main program section */
int main( int argc, char *argv[] )
{
int a = 36;
int b = 24;
int c = 0;
printf( "hi mom\n" );
/* now call a FORTRAN function from C */
c = NGCD( &a, &b );
printf( "NGCD(%d,%d)=%d\n", a, b, c );
return 0;
}
The fortran function which will most often contain FORTRAN 77 (but
can also include FORTRAN90/95 code too), looks like:
c$$$ The following introductory example in FORTRAN 77 finds the
c$$$ $ greatest common divisor for two numbers A and B using a
c$$$ $ verbatim implementation of Euclid's algorithm.
FUNCTION NGCD(NA, NB)
IA = NA
IB = NB
1 IF (IB.NE.0) THEN
ITEMP = IA
IA = IB
IB = MOD(ITEMP, IB)
GOTO 1
END IF
NGCD = IA
RETURN
END
Using Dev. Studio 6/Compaq Digital Fortran 6.0, this works fine. In
fact, I don't have to use the #define ifdef __cplusplus/#endif and can
simply create a C file that looks like:
/* this is a simple program */
#include <stdio.h>
/* add the extern function definition */
extern "C" int __stdcall NGCD( int *a, int *b );
/* this is the main program section */
int main( int argc, char *argv[] )
{
int a = 36;
int b = 24;
int c = 0;
printf( "hi mom\n" );
/* now call a FORTRAN function from C */
c = NGCD( &a, &b );
printf( "NGCD(%d,%d)=%d\n", a, b, c );
return 0;
}
and compile it with the FORTRAN listing above, the application links,
runs, and generates the correct results.
C:\fooonly>fooonly.exe
hi mom
NGCD(36,24)=12
C:\fooonly>
When I try top repeat this process using GNU Autotools on MinGW or
OSX, I continue to get the following errors:
macbook:makecheck $ make
gcc -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE=\"fooonly\" -DVERSION=\"1.0.2\" -I. -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c
mv -f .deps/main.Tpo .deps/main.Po
gfortran -g -O2 -o fooonly main.o ngcd.o
Undefined symbols for architecture x86_64:
"_NGCD", referenced from:
_main in main.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make: *** [fooonly] Error 1
macbook:makecheck $
where the Makefile (generated by GNU Autotools), basically contains
the following commands:
macbook:makecheck $ gcc -c main.c
macbook:makecheck $ gfortran -c ngcd.f
macbook:makecheck $ gcc -o fooonly main.c ngcd.o
Undefined symbols for architecture x86_64:
"_NGCD", referenced from:
_main in cc9uPBWl.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
macbook:makecheck $
My configure.in script contains nothing more than:
AC_INIT(main.c)
AM_INIT_AUTOMAKE(fooonly, 1.0.2)
## C/C++ compiler section
AC_PROG_CC
## fortran section
AC_PROG_F77
## output section
AC_OUTPUT(Makefile)
which is essentially,
macbook:makecheck $ gcc -c main.c
macbook:makecheck $ gfortran -c ngcd.f
macbook:makecheck $ gcc -o fooonly main.c ngcd.o
right?
I'm trying to build this on multiple platforms (Linux, Win32/64, OSX,
etc.) and wish to use GNU Autotools, and I know this is done with
other open-source projects, but the configure.in scripts for those
projects are way beyond my GNU Autotools newbie chops and I get a
little overwhelmed trying to decode them.
I'm guessing this has something to do with:
1) The definitions I've used in the configure.in script,
2) I'm not including some magical set of switches
(i.e. -fno-second-underscore?), or
3) Some combination of the two?
Am I close and if so, how do I get the app to build?
As long as you have a compiler newer then the past several years, I recommend using the ISO C Binding to mix Fortran with other languages. Then you can skip the name mangling with underscores and similar compiler/platform dependent issues. If you have legacy FORTRAN 77 code that you don't want to alter, you could write a small Fortran 2003 glue routine between the C and the FORTRAN 77. Older instructions describe the previous method which required more understanding of the internal interfaces and was more compiler/platform dependent. For the new method, look at the gfortran manual chapter "Mixed Language Programming" and previous questions/answers here.
With Fortran code it is easier to link with gfortran because that brings in the Fortran runtime libraries. I think that the same applies to C++, so if you have both you will have to explicitly include the runtime library of one or the other.
P.S. Here is an example using the Fortran ISO C Binding:
function NGCD (na, nb) bind (C, name="NGCD")
use iso_c_binding
implicit none
integer (c_int) :: ngcd
integer (c_int), intent (in) :: na, nb
integer (c_int) :: ia, ib, itemp
ia = na
ib = nb
do while (ib /= 0)
itemp = ia
ia = ib
ib = mod(itemp, ib)
end do
ngcd = ia
return
end function NGCD
Compile/link with:
gcc -c main.c
gfortran main.o ngcd.f90
Related
Sample code for fmod:
#include <stdio.h>
#include <math.h>
int main(void)
{
double x = 0.14527, y = 3.14159;
printf("fmod(x, y) = %.6lf\n", fmod(x, y));
return 0;
}
Compiling:
$ gcc main.c -o main
I get
/tmp/ccztJO01.o: In function `main':
main.c:(.text+0x4d): undefined reference to `fmod'
collect2: ld returned 1 exit status
Then I found this in Google:
$ gcc -lm main.c -o main
Why should I use -lm, what is it exactly? From where I can get more information about gcc in detail?
-lm is simply telling it to link libm, which contains all the floating point math routines, including (no surprise here) fmod.
When I input gcc -lm main.c -o main I still get a linker error. I need to write gcc main.c -lm -o main for it work right. If it's working for you the other way, that's a bit odd. I understand that the linker will find the symbol declared in main.c (i.e. double fmod(double,double)), but only resolve it if it finds its definition later on (i.e. in libm.a).
Long story short, the libraries must be placed (at least once) "to the right of" the place where they are used.
It's not the compiler, but the linker, ld, that is complaining. It cannot find the routine fmod in your program. You have to tell it to link with math library libm with the -l flag.
[Much] more info: GCC, the GNU Compiler Collection.
My objective is to have a Delphi( or freepascal) code, that will call the C function func like this one:
The C/Cuda file:
/* this is the "progcuda.cu" file */
#include <stdio.h>
__global__ void foo(int *a, int *b, int *c, int n){
/*
add all the vector's element
*/
}
void func(int *a, int *b, int *c,int n){
int *da,*db,*dc;
cudaMalloc(&da, n*sizeof(int));
cudaMalloc(&db, n*sizeof(int));
cudaMalloc(&dc, n*sizeof(int));
cudaMemcpy(da,a,sizeof(int)*n,cudaMemcpyHostToDevice);
cudaMemcpy(db,b,sizeof(int)*n,cudaMemcpyHostToDevice);
cudaMemcpy(dc,c,sizeof(int)*n,cudaMemcpyHostToDevice);
foo<<<1,256>>>(da,db,dc);
cudaMemcpy(c,dc,sizeof(int),cudaMemcpyDeviceToHost);
/* do other stuff and call another Host and Device functions*/
return;
}
The pascal main file:
// this is the "progpas.pas" file
program progpas;
{$mode objfpc}{$H+}
uses unitpas;
var
...
begin
...
func(a, b, c, len);
...
end.
The pascal unit file:
// this is the "unitpas.pas" file
unit unitpas;
{$link progcuda.o}
interface
uses ctypes;
procedure func(a, b, c : cpint32 , n:cint32); cdecl; external;
procedure foo(a, b, c : cpint32 , n:cint32);cdecl; external;
implementation
end.
I've found this post Programming CUDA using Delphi or FreePascal
, but it shows more a way to program CUDA in delphi.
I don't want to program CUDA in Delphi, I want to program in CUDA in pure C/C++ code and only call that C function in delphi.
What is the problem?
How can I link the .cu code to the delphi one?
I'm using linux ubuntu 16.04 LTS, but I also have CUDA and VS in windows if necessary.
Note: if you guys could explain in detail how to do it, would help ( new to pascal and linking files )
I've already tried to generate the .o object file and link it in free pascal with
$ nvcc progcuda.cu -c -o progcuda.o then $fpc progpas.pas
but it fails at linking.
Note: I've tried once to link a normal .o generated by C code to pascal code, using gcc and freepascal compiler, and it worked, but if I use nvcc instead of gcc and rename the extension to .cu ( still same code), the linking fails.
note: new account in stack overflow, i cannot repply answers yet.
I don't know anything about Delphi and FreePascal, but I do know about CUDA, C and C++, so maybe my solution will also work for you.
I'll be demonstrating it with a simple problem:
Content of f.cu:
int f() { return 42; }
Content of main.c:
extern int f();
int main() {
return f();
}
The following works:
$ gcc -c -xc f.cu # need -xc to tell gcc it's a C file
$ gcc main.c f.o
(no errors emitted)
Now when we try replacing gcc with nvcc:
$ nvcc -c f.cu
$ gcc main.c f.o
/tmp/ccI3tBM1.o: In function `main':
main.c:(.text+0xa): undefined reference to `f'
f.o: In function `__cudaUnregisterBinaryUtil()':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0x52): undefined reference to `__cudaUnregisterFatBinary'
f.o: In function `__nv_init_managed_rt_with_module(void**)':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0x6d): undefined reference to `__cudaInitModule'
f.o: In function `__sti____cudaRegisterAll()':
tmpxft_0000704e_00000000-5_f.cudafe1.cpp:(.text+0xa9): undefined reference to `__cudaRegisterFatBinary'
collect2: error: ld returned 1 exit status
The problem here is that nvcc adds references to some symbols from the CUDA runtime API when compiling f.cu, and these symbols have to be linked to the final executable. My CUDA installation is in /opt/cuda, so I will use that, but you have to replace it with wherever CUDA is installed on your system. So if we link libcudart.so when compiling the library we get:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart
/tmp/ccUeDZcb.o: In function `main':
main.c:(.text+0xa): undefined reference to `f'
collect2: error: ld returned 1 exit status
This looks better, no strange errors, but it's still not finding the function f. That's because nvcc is treating f.cu as a C++ file, so it does name mangling when creating the object file, and we have to specify that we want f to have C, and not C++ linkage (see more here: http://en.cppreference.com/w/cpp/language/language_linkage).
To do that we have to modify f.cu like this:
extern "C" int f() { return 42; }
Now when we do:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart
(no errors emitted)
I hope you manage to modify this to work with your language.
EDIT: I tried a bit more complicated example:
// f.cu
#include <stdio.h>
__global__ void kernel() {
printf("Running kernel\n");
}
extern "C" void f() {
kernel<<<1, 1>>>();
// make sure the kernel completes before exiting
cudaDeviceSynchronize();
}
// main.c
extern void f();
int main() {
f();
return 0;
}
When compiling it I got:
f.o:(.data.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0]+0x0): undefined reference to `__gxx_personality_v0'
collect2: error: ld returned 1 exit status
To fix it you also need to add the standard C++ libraries to the linker flags:
$ nvcc -c f.cu
$ gcc main.c f.o -L/opt/cuda/lib64 -lcudart -lstdc++
$ ./a.out
Running kernel
I fixed the files as #Goran Flegar explained:
Add extern "C" int func(...); to the .cu file. And then tried to compile/link the .cu code, but with no device calls (yet with device code), and all worked well.
but when i add a device call ( foo<<<Nb,Nt>>>(...) ) and compile with:
$nvcc progcuda.cu -c
$fpc progpas.pas -ofinal.exe -Fl/usr/local/cuda/lib64
i get:
Free Pascal Compiler version 3.0.4 [2017/12/13] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling prog1.pas
Linking sum.exe
/usr/bin/ld: aviso: link.res contém seções de saída; você se esqueceu -T?
/usr/bin/ld: sum.o: undefined reference to symbol '_Unwind_Resume##GCC_3.0'
//lib/x86_64-linux-gnu/libgcc_s.so.1: error adding symbols: DSO missing from command line
prog1.pas(16,1) Error: Error while linking
prog1.pas(16,1) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode
So there's still some missing libs.
Solution:
Found that linking the stdc++ and gcc_s lib to pascal solved the compilation problem.
unit unitpas;
// file "unitpas.pas"
{$LINK progcuda.o}
{$LINKLIB c}
{$LINKLIB cudart}
{$linklib stdc++}
{$linklib gcc_s}
interface
uses ctypes;
function func(x,y: cint32): cint32; cdecl; external;
implementation
end.
Run
$nvcc progcuda.cu -c
$fpc progpas.pas -ofinal.exe -Fl/usr/local/cuda/lib64
and everything works.
I'm trying to implement a simple integration of R with C. Initially it's simple: I want to pass values from R to a C function built into a .o shared library via .C or .Call function. The C function should simply print the values passed in via printf.
Here's my .Call method:
.Call("test", as.integer(5), as.character("A"), as.character("string_test"))
And my C code:
#include <stdio.h>
void test(int integer, char character, char **str) {
printf("Integer: %i\nChar: %c\nString: %s\n", integer, character, *str);
}
But when I call the C function from R via console (RStudio crashes) with gdb enabled, I receive:
Integer: 1466480376
Char: �
Float: -100407552.000000
String:
***caught segfault ***
address 0x20000090, cause 'memory not mapped'
Traceback:
1: .Call("test", as.integer(5), as.character("A"), as.character("string_test"))
As if it were not enough, as we can see the values passed in are printed very strangely.
Details of what I did, step by step:
I built the .o shared library with gcc:
gcc -shared -o func_teste.o -fPIC func_teste.c
And prepared it for dynamic loading in R environment:
$ R CMD SHLIB func_teste.o
gcc -m64 -I/usr/include/R -DNDEBUG -I/usr/local/include -fpic -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -c func_teste.c -o func_teste.o
gcc -m64 -shared -L/usr/lib64/R/lib -Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -o func_teste.so func_teste.o -L/usr/lib64/R/lib -lR
And finally, inside R console, i ran:
>dyn.load('func_teste.o')
>.Call("test", as.integer(5), as.character("A"), as.character("string_test"))
Does anyone have idea why this is happening?
R offers two main functions for interfacing from C code (and hence C++ code, or any other language able to use a C interface):
- .C() is the older interface using int*, double*, ... and alike
- .Call() is the newer, more powerful interface using SEXP objects
Now, .Call() looks more complicated but it is so much more powerful as well as safer. There is near universal consensus that .C() should no longer be used (see various discussions on the r-devel list and other places).
The main downside with .Call() is that you need to learn how to pack and unpack your values. Or ... you cheat and let Rcpp do it for you. So with that, here is one-line solution of the OP's example:
> library(Rcpp)
> cppFunction("void mytest(int i, char c, std::string str) { printf(\"Integer: %i Char: %c String: %s\\n\", i, c, str.c_str()); }")
> mytest(42L, 'Q', "a boat")
Integer: 42 Char: Q String: a boat
>
I made the char* a string. Note that cppFunction() requires escaping of strings, you may want to look into sourceCpp() and packages for real work. The Rcpp documentation has details.
Don't as.character on "string_test".
Read more here: http://mazamascience.com/WorkingWithData/?p=1067
I am trying to learn how to interface Haskell and C. To start it out, I wrote Inc.hs, the simplest thing I can conceive:
{-# LANGUAGE ForeignFunctionInterface #-}
module Inc where
import Foreign
import Foreign.C.Types
inc :: Int -> Int
inc = (+1)
foreign export ccall cinc :: CInt -> CInt
cinc :: CInt -> CInt
cinc = fromIntegral . inc . fromIntegral
And compiled it to produce Inc_stub.h:
ghc -c Inc.hs
Worked fine. I then wrote the C file, also trying to be as simple as humanly possible:
#include <stdio.h>
#include "Inc_stub.h"
int main(int argc, char *argv[]) {
int a = 1;
hs_init(&argc, &argv);
a = cinc(a);
hs_exit();
if (a == 2) {
puts("Worked!");
}
return 0;
}
Tried to compile it, got this linker error:
ghc -no-hs-main inc.c Inc -o simplest
Linking simplest.exe ...
inc.o:inc.c:(.text+0x0): multiple definition of `main'
Inc.o:inc.c:(.text+0x0): first defined here
Inc.o:inc.c:(.text+0x31): undefined reference to `cinc'
c:/program files/haskell platform/7.10.2-a/mingw/bin/../lib/gcc/x86_64-w64-mingw32/4.6.3/../../../../x86_64-w64-mingw32/bin/ld.exe: Inc.o: bad reloc address 0x0 in section `.pdata'
collect2: ld returned 1 exit status
Everything was compiled using GHC 7.10.2 on Windows 10 64-bit.
I did the following:
renamed inc.c to inc_main.c because having C object file inc.o might overlap with haskell object
run ghc -c -no-hs-main Inc.hs -o Inc.o
Produce C object file by gcc -O -Wall -I/usr/lib/ghc/include -c inc_main.c
Link to an executable with ghc -no-hs-main Inc.o inc_main.o -o simplest
This is just explanation of what's going on, see #Hakala's answer for the solution.
The problem is that on Windows file names are case insensitive.
When you are executing
$ ghc -no-hs-main inc.c Inc -o simplest
GHC calls GCC to compile inc.c into object file inc.o. But on Windows it also overwrites Inc.o generated by ghc -c Inc.hs. So, it is actually the same as executing
$ ghc -no-hs-main inc.c inc.o -o simplest
And linking inc.o twice will obviously result in "multiple definition" error.
I have a FORTRAN code which calls a C routine to calculate a measure. The FORTRAN code is:
program fortran
implicit none
interface
double precision function fpli_hv(A, d, n)
real :: A(5,3)
integer :: d, n
end function fpli_hv
end interface
real :: A(5,3)
double precision :: HV
integer :: i, j
A(1,:) = (/1.1,3.2,2.0/)
A(2,:) = (/6.3,5.2,7.2/)
A(3,:) = (/3.3,4.4,9.1/)
A(4,:) = (/3.3,5.2,2.1/)
A(5,:) = (/7.6,1.7,4.3/)
HV = fpli_hv(A, 3, 5)
end program fortran
The c function looks like this:
double fpli_hv(double *front, int d, int n, double *ref);
In order to club c and fortran, I need to include a Makefil.lib in my makefile. I did so, and prepared my makefile as follows:
# The makefile should contain a set of suffix rules. All suffixes must
# be defined. In this case we will have .o for object files, .c for
# C files, and .f for Fortran files.
.SUFFIXES: .o .c .f90
# LIBRARY:
LIBHV = /gpfs0/home/shafiiha/programs/hv-2.0rc1-src/fpli_hv.a
#include Makefile.lib
# Define the C and Fortran compilers to be used in this makefile:
CC=
FC=gfortran -ffree-form -c
# Define flags to be used by the C and Fortran compilers:
CFLAGS =
FFLAGS =
# Define include to be used by the C and Fortran compilers:
C_INCLUDES =
F_INCLUDES = fortran.f90
# The linker executable in this case must be the MPI Fortran compiler
# to build a mixed C and Fortran MPI code:
LINK = gfortran
# Define values of parameters that appear in the source codes:
DEFINES =
# Define the list of object files for the linker. The linker will use
# those files to build the executable.
OBJECTS = fortran.o
# The rule that makes the drv executable (note that libraries have
# been specified by the mpif90 linker):
fortran: $(OBJECTS)
$(LINK) -o fortran $(OBJECTS) $(LIBHV)
# The rule that makes all object files from C sources:
.c.o:
$(CC) $(CFLAGS) $(C_INCLUDES) $(DEFINES) $<
# The rule that makes all object files from Fortran sources:
.f90.o:
$(FC) $(FFLAGS) $(F_INCLUDES) $^ $(LIBHV)
# The rule for deleting object files no longer needed after using
# make for drv:
clean:
rm *.o
But when I make it, I get this message:
gfortran -o fortran fortran.o /gpfs0/home/shafiiha/programs/hv-2.0rc1-src/fpli_hv.a
fortran.o: In function `MAIN__':
fortran.f90:(.text+0x548): undefined reference to `fpli_hv_'
collect2: ld returned 1 exit status
make: *** [fortran] Error 1
Could you please help me why I get this error? Thanks a lot.
In this era the best way to call C from Fortran is to use the ISO C Binding. Your problem is the name mangling that Fortran does by default to avoid collisions with routines of C or standard libraries, typically adding underscores. With the ISO C Binding you can both specify the exact name of the called routine, overriding name mangling, and easily achieve Fortran-C consistency on the arguments. On the Fortran side you write an interface describing the C routine. There have been previous answers here and there are examples in the gfortran manual. The examples aren't unique to gfortran since the ISO C Binding is part of the Fortran 2003 language standard.
If you're doing interfaces already (which is great!) then you may as well use the iso_c_binding module which the newest versions of most fortran compilers already have to avoid worrying about underscores and letter cases and what have you:
interface
function fpli_hv(A, d, n) bind(C)
use iso_c_binding
implicit none
real(kind=c_double) :: fpli_hv
integer(kind=c_int), value :: d, n
real(kind=c_float) :: A(5,3)
end function fpli_hv
end interface
Wrap
double fpli_hv(double *front, int d, int n, double *ref);
with
void FPLI_HV_(double *front, int* d, int* n, double *ref, double* returnvalue);
Fortran objects only understand things ending in _ and all function passes are by pointer.
(Edit for below comment).