Compiling C and Fortran using iFort - c

I am trying to call a simple "Hello World" code from C using Fortran. When I compile using iFort, it is giving me "error LNK2019: unresolved external symbol HELLO referenced in function MAIN__"
C Code:
#include <stdio.h>
void hello_(){
printf("Hello World :) \n");
}
Fortran Code:
program Fortran_C_Link_Test
C
implicit None
C
call hello()
C
stop
end
How I Compiled:
cl -c c_src.c
to generate the c_src.obj object file
ifort -c fortran_src.f
to generate the fortran_src.obj object file
ifort -o program c_src.obj fortran_src.obj
to generate the executable

Related

Run C function with CUDA calls in Delphi program

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.

clang++ (version 5) and LNK4217 warning

I am just learning how to code.
I have installed clang version 5 on a windows 10 system using visual studio 14.
I created a hello world cpp file to test that is working.
Sample code
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World!\n";
int rip{1};
int dal{4};
int kane = rip + dal;
cout << kane;
return 0;
}
command
clang++ -o .\bin\testing.exe test.cpp
Clang does compile and I get an executable which does run as expected. however I do receive this message.
test-3e53b3.o : warning LNK4217: locally defined symbol ___std_terminate imported in function "int `public: __thiscall std::basic_ostream<char,struct std::char_traits<char> >::sentry::~sentry(void)'::`1'::dtor$5" (?dtor$5#?0???1sentry#?$basic_ostream#DU?$char_traits#D#std###std##QAE#XZ#4HA)
test-3e53b3.o : warning LNK4217: locally defined symbol __CxxThrowException#8 imported in function "public: void __thiscall std::ios_base::clear(int,bool)" (?clear#ios_base#std##QAEXH_N#Z)
I have searched online and can find similar issues but they are not the same.
I realise this maybe simple to you guys, but I am at a loss I have used various IDES and GCC and this code has not produced this warning before.
Add -Xclang -flto-visibility-public-std to your compiler options.
Like so:
clang++ -Xclang -flto-visibility-public-std -o test.exe test.cpp
Edit:
Or use clang-cl instead:
clang-cl -o test.exe test.cpp

undefined reference error for linking CUDA static or shared library with gcc

gcc and CUDA question
Hi,
I have compiled a CUDA shared library but can't link it with the main program that uses it. I am compiling the main program with gcc.
The code:
simplemain.c
#include <stdio.h>
#include <stdlib.h>
void fcudadriver();
int main()
{
printf("Main \n");
fcudadriver();
return 0;
}
test.cu
__global__ void fcuda()
{
}
void fcudadriver()
{
fcuda<<<1,1>>>();
}
I compile test.cu as --> It works
nvcc --compiler-options '-fPIC' -o libtest.so --shared test.cu
I compile simplemain.c as ---> It gives error :(
gcc simplemain.c -L. -ltest
/tmp/ccHnB4Vh.o:simplemain.c:function main: error: undefined reference to 'fcudadriver'
collect2: ld returned 1 exit status
try using g++ instead of gcc. nvcc uses c++ style linking conventions. (You don't need to rename any files.)
alternatively, if you must use gcc, preface your void fcudadriver() function definition like this:
extern "C" void fcudadriver()
C and C++ name the functions in different way.
Since nvcc treat the CPU code in .cu file as C++, you could rename your simplemain.c to simplemain.cpp, and compile it with g++
Another solution could be adding extern "C" before the function definition in the .cu file.

C/C++, FORTRAN, underscores, and GNU Autotools

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

Error when compiling with GCC

Every time I compile I get the following error message:
Undefined reference to ( function name )
Let's say I have three files: Main.c, printhello.h, printhello.c. Main.c calls function print_hello(), which returns "Hello World". The function is defined in printhello.c.
Now, here's the following code of printhello.h:
#ifndef PRINTHELLO_H
#define PRINTHELLO_H
void print_hello();
#endif
I am sure this code is fine. I still don't know why is it giving me the error, though. Can you help me?
Undefined references are the linker errors. Are you compiling and linking all the source files ? Since the main.c calls print_hello(), linker should see the definition of it.
gcc Main.c printhello.c -o a.out
The error is, I think, a linker error rather than a compiler error; it is trying to tell you that you've not provided all the functions that are needed to make a complete program.
You need to compile the program like this:
gcc -o printhello Main.c printhello.c
This assumes that your file Main.c is something like:
#include "printhello.h"
int main(void)
{
print_hello();
return 0;
}
and that your file printhello.c is something like:
#include "printhello.h"
#include <stdio.h>
void print_hello(void)
{
puts("Hello World");
}
Your declaration in printhello.h should be:
void print_hello(void);
This explicitly says that the function takes no parameters. The declaration with the empty brackets means "there is a function print_hello() which returns no value and takes an indeterminate (but not variadic) list of arguments", which is quite different. In particular, you could call print_hello() with any number of arguments and the compiler could not reject the program.
Note that C++ treats the empty argument list the same as void print_hello(void); (so it would ensure that calls to print_hello() include no arguments), but C++ is not the same as C.
Another way to do it is to explicitly build object files for the printhello:
gcc -c printhello.c -o printhello.o
gcc -o Main main.c printhello.o
This has the added benefit of allowing other programs to use the print_hello method
It seems that the error is from the linker and not the compiler. You need to compile and link both the source files. I think what you are doing is simply including the header file in Main.c and you are not compiling the printhello.c
You need to :
gcc Main.c printhello.c -o myprog
or
construct the object files first
gcc -c printhello.c
gcc -c Main.c
then link them
gcc Main.o printhello.o

Resources