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.
Related
How to change the entry point of a C program compiled with gcc ?
Just like in the following code
#include<stdio.h>
int entry() //entry is the entry point instead of main
{
return 0;
}
It's a linker setting:
-Wl,-eentry
the -Wl,... thing passes arguments to the linker, and the linker takes a -e argument to set the entry function
You can modify your source code as:
#include<stdio.h>
const char my_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
int entry() //entry is the entry point instead of main
{
exit(0);
}
The ".interp" section will let your program able to call external shared library.
The exit call will make your entry function to exit program instead of return.
Then build the program as a shared library which is executable:
$ gcc -shared -fPIC -e entry test_main.c -o test_main.so
$ ./test_main
If you are on a system that provides GNU Binutils (like Linux),
you can use the objcopy command
to make an arbitrary function the new entry point.
Suppose a file called program.c containing the entry function:
$ cat > program.c
#include <stdio.h>
int entry()
{
return 0;
}
^D
You first compile it using -c to generate a relocatable object file:
$ gcc -c program.c -o program.o
Then you redefine entry to be main:
$ objcopy --redefine-sym entry=main program.o
Now use gcc to compile the new object file:
$ gcc program.o -o program
NOTE: If your program already has a function called main, before step 2, you can perform a separate objcopy invocation:
objcopy --redefine-sym oldmain=main program.o
Minimal runnable example and notes on other answers
main.c
#include <stdio.h>
#include <stdlib.h>
int mymain(void) {
puts("hello");
exit(0);
}
compile and run:
gcc -nostartfiles -Wl,--entry=mymain -o main.out main.c
# or -Wl,-emymain
./main.out 1 2 3
The notes:
without -nostartfiles, the link fails with:
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
presumably because the glibc setup code that runs before main in _start normally calls main.
command line arguments are not setup for you, presumably because they would be setup by the glibc code that runs before main, so trying to use them prints undefined values. I haven't found a method that works for them.
Tested in Ubuntu 20.10.
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 am trying to compile several literate haskell (.lhs) files to a shared object (.so), and then to link it with a main written in c. The issue here, though, is that 2 of the files used to create the .so are template haskell. i followed the rules for compiling a .so with template haskell, which means that i did the following steps:
1. I compiled each .lhs files ,statically
2. Then i compiled them all second time dynamically.
3. I created the shared object from the object files i got from steps 1 & 2.
4. I compiled the main.c into main.o
5. I created an executable from steps 3 & 4.
there are 3 files from which the .so is created. Dep1.lhs, Dep2.lhs & Dep3.lhs, and a main written in c
when i compile makefile i get this message:
my_directory >> make all
rm -f *.o *.hi *.so *.dyn_hi *.dyn_o main
ghc -c Dep3.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep3.o
ghc -c Dep3.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep3.dyn_o -osuf dyn_o -hisuf dyn_hi
ghc -c Dep2.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep2.o
ghc -c Dep2.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep2.dyn_o -osuf dyn_o -hisuf dyn_hi
ghc -c Dep1.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep1.o
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package containers-0.5.0.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
ghc -c Dep1.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep1.dyn_o -osuf dyn_o -hisuf dyn_hi
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package pretty-1.1.1.0 ... linking ... done.
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package containers-0.5.0.0 ... linking ... done.
Loading package template-haskell ... linking ... done.
ghc -O2 -dynamic -shared -fPIC Dep1.dyn_o Dep2.dyn_o Dep3.dyn_o -o libShared.so -lHSrts-ghc7.6.3
gcc -O2 -I/usr/local/lib/ghc-7.6.3/include -L/usr/local/lib/ghc-7.6.3 -L/usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0/ -c Main.c -o main.o
gcc -o main main.o -L/usr/local/lib/ghc-7.6.3 -L/usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0/ -L. -lShared -Wl,-rpath,/usr/local/lib/ghc-7.6.3 -L/home/tal/a_prerequisites/new_haskell/ghc-7.6.3/libraries/base/dist-install/build/libHSbase-4.6.0.1-ghc7.6.3. -lHStemplate-haskell-2.8.0.0
/usr/bin/ld: dynamic variable `ghczmprim_GHCziTypes_True_closure' is zero size
/usr/bin/ld: dynamic variable `ghczmprim_GHCziTypes_ZMZN_closure' is zero size
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Syntax__1744.o)(.text+0x77): unresolvable R_X86_64_32 relocation against symbol `ghczmprim_GHCziTypes_True_closure'
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Lib__228.o)(.text+0x14): unresolvable R_X86_64_32S relocation against symbol `ghczmprim_GHCziTypes_ZMZN_closure'
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Lib__137.o)(.text+0x14): unresolvable R_X86_64_32S relocation against symbol `ghczmprim_GHCziTypes_ZMZN_closure'
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Lib__227.o)(.text+0x14): unresolvable R_X86_64_32S relocation against symbol `ghczmprim_GHCziTypes_ZMZN_closure'
/usr/bin/ld: /usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0//libHStemplate-haskell-2.8.0.0.a(Lib__124.o)(.text+0x14): unresolvable R_X86_64_32S relocation against symbol `ghczmprim_GHCziTypes_ZMZN_closure
and an executable 'main' is created, but when i try to run it, the following occurs:
host113#/home/tal/Documents/mfbus >> main
main: error while loading shared libraries: libHSbase-4.6.0.1-ghc7.6.3.so: cannot open shared object file: No such file or directory
i tried to include in the last rule (main) the directory of ' libHSbase-4.6.0.1-ghc7.6.3.so' in the '-l' option so that it will load it.but it doesnt seem to work. May someone have an insight to the error?
the code for Dep1.lhs:
> {-# LANGUAGE TemplateHaskell #-} <br/>
> {-# LANGUAGE ForeignFunctionInterface #-} <br/>
> module Dep1 where
> import Foreign <br/>
> import Foreign.C.Types <br/>
> import Dep3 <br/>
> data MyData = MyData
> { foo :: String
> , bar :: Int
> }
> emptyShow ''MyData
> foreign export ccall some_func :: IO () <br/>
> foreign export ccall factorial :: Int -> Int
> some_func :: IO () <br/>
> some_func = print $ MyData { foo = "bar", bar = 5 }
> factorial :: Int -> Int <br/>
> factorial 0 = 1 <br/>
> factorial n = n *(factorial $ n - 1)
the code for Dep3.lhs (comes here because Dep1.lhs imports it):
> {-# LANGUAGE TemplateHaskell, FlexibleInstances #-}
> module Dep3 where
> import Language.Haskell.TH
> emptyShow :: Name -> Q [Dec] <br/>
> emptyShow name = [d|instance Show $(conT name) where show _ = "some meaningful sentence"|]
the code for Dep2.lhs:
> {-# LANGUAGE ForeignFunctionInterface #-}
> module Dep2 where <br/>
> import Foreign <br/>
> import Foreign.C.Types
> foreign export ccall power :: CInt -> CInt
> power :: CInt -> CInt
> power n = n*n
the code for Main.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <HsFFI.h>
#ifdef __GLASGOW_HASKELL__
#include "Tal2_stub.h"
#endif
#ifdef __GLASGOW_HASKELL__
extern void __stginit_power ( void );
#endif
// int power(int i){ return i*i; }
int fact(int i){
if (i == 0) return 1;
else return i * fact(i-1);
}
nt main(int argc, char *argv[]){
hs_init(&argc, &argv);
#ifdef __GLASGOW_HASKELL__
hs_add_root(__stginit_power);
#endif
printf("what is 5!?\n");
char buf[2048];
scanf("%s",buf);
int x = atoi(buf);
if(x == fact(5)){
printf("You're right!\n");
} else {
printf("You're wrong!\n");
}
printf("what is the power of 2?\n");
scanf("%s",buf);
x = atoi(buf);
if(x == power(2)){
printf("You're right!\n");
} else {
printf("You're wrong!\n");
}
hs_exit();
return 0;
}
my makefile code:
all : clean main
main : shared main.o
gcc -o main main.o -L/usr/local/lib/ghc-7.6.3 -L/usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0/ -L. -lShared -Wl,-rpath,/usr/local/lib/ghc-7.6.3 -L/home/tal/a_prerequisites/new_haskell/ghc-7.6.3/libraries/base/dist-install/build/libHSbase-4.6.0.1-ghc7.6.3. -lHStemplate-haskell-2.8.0.0
main.o :
gcc -O2 -I/usr/local/lib/ghc-7.6.3/include -L/usr/local/lib/ghc-7.6.3 -L/usr/local/lib/ghc-7.6.3/template-haskell-2.8.0.0/ -c Main.c -o main.o
shared : dep3second dep2second dep1second
ghc -O2 -dynamic -shared -fPIC Dep1.dyn_o Dep2.dyn_o Dep3.dyn_o -o libShared.so -lHSrts-ghc7.6.3
dep1second : dep1first
ghc -c Dep1.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep1.dyn_o -osuf dyn_o -hisuf dyn_hi
dep2second : dep2first
ghc -c Dep2.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep2.dyn_o -osuf dyn_o -hisuf dyn_hi
dep3second: dep3first
ghc -c Dep3.lhs -dynamic -XTemplateHaskell -fPIC -no-hs-main -XForeignFunctionInterface -o Dep3.dyn_o -osuf dyn_o -hisuf dyn_hi
dep1first :
ghc -c Dep1.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep1.o
dep2first :
ghc -c Dep2.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep2.o
dep3first :
ghc -c Dep3.lhs -XTemplateHaskell -XForeignFunctionInterface -o Dep3.o
.PHONY : clean
clean :
-rm -f *.o *.hi *.so *.dyn_hi *.dyn_o main
Note: my knowledge of Haskell is limited and this answer doesn't assume anything about Haskell at all.
The error says:
<executable>: error while loading shared libraries: <shared lib>.so: cannot open shared object file: No such file or directory
which means that when trying to execute your program, the shared object wasn't found. Note the difference between finding the shared object during link and during execution.
Roughly speaking, when you use -L and -l to link to a shared object, the link is not completely done, but remembered to be done at runtime. When you execute the executable that depends on that shared object, somebody else (not the compiler) must be able to find the shared object and do the linking: again roughly speaking, Linux.
So you need to have Linux find your shared object too. There are several ways to do so, some of which are deprecated. The way I would recommend is to write a file with the path of the shared object and put it under /etc/ld.so.conf.d/. Then, execute ldconfig as root (e.g. with sudo) and Linux should be able to find the shared object.
I haven't tested this, but if you just edit /etc/ld.so.conf and add a line containing ., the Linux should be able to find the shared object if it's located in the same directory the executable is being executed from. This could be ok for development, but I would not recommend it if the shared object is to be installed.
So I'm trying trying to use a function defined in another C (file1.c) file in my file (file2.c). I'm including the header of file1 (file1.h) in order to do this.
However, I keep getting the following error whenever I try to compile my file using gcc:
Undefined symbols for architecture x86_64:
"_init_filenames", referenced from:
_run_worker in cc8hoqCM.o
"_read_list", referenced from:
_run_worker in cc8hoqCM.o
ld: symbol(s) not found for architecture x86_64
I've been told I need to "link the object files together" in order to use the functions from file1 in file2, but I have no clue what that means :(
I assume you are using gcc, to simply link object files do:
$ gcc -o output file1.o file2.o
To get the object-files simply compile using
$ gcc -c file1.c
this yields file1.o and so on.
If you want to link your files to an executable do
$ gcc -o output file1.c file2.c
The existing answers already cover the "how", but I just wanted to elaborate on the "what" and "why" for others who might be wondering.
What a compiler (gcc) does: The term "compile" is a bit of an overloaded term because it is used at a high-level to mean "convert source code to a program", but more technically means to "convert source code to object code". A compiler like gcc actually performs two related, but arguably distinct functions to turn your source code into a program: compiling (as in the latter definition of turning source to object code) and linking (the process of combining the necessary object code files together into one complete executable).
The original error that you saw is technically a "linking error", and is thrown by "ld", the linker. Unlike (strict) compile-time errors, there is no reference to source code lines, as the linker is already in object space.
By default, when gcc is given source code as input, it attempts to compile each and then link them all together. As noted in the other responses, it's possible to use flags to instruct gcc to just compile first, then use the object files later to link in a separate step. This two-step process may seem unnecessary (and probably is for very small programs) but it is very important when managing a very large program, where compiling the entire project each time you make a small change would waste a considerable amount of time.
You could compile and link in one command:
gcc file1.c file2.c -o myprogram
And run with:
./myprogram
But to answer the question as asked, simply pass the object files to gcc:
gcc file1.o file2.o -o myprogram
Add foo1.c , foo2.c , foo3.c and makefile in one folder
the type make in bash
if you do not want to use the makefile, you can run the command
gcc -c foo1.c foo2.c foo3.c
then
gcc -o output foo1.o foo2.o foo3.o
foo1.c
#include <stdio.h>
#include <string.h>
void funk1();
void funk1() {
printf ("\nfunk1\n");
}
int main(void) {
char *arg2;
size_t nbytes = 100;
while ( 1 ) {
printf ("\nargv2 = %s\n" , arg2);
printf ("\n:> ");
getline (&arg2 , &nbytes , stdin);
if( strcmp (arg2 , "1\n") == 0 ) {
funk1 ();
} else if( strcmp (arg2 , "2\n") == 0 ) {
funk2 ();
} else if( strcmp (arg2 , "3\n") == 0 ) {
funk3 ();
} else if( strcmp (arg2 , "4\n") == 0 ) {
funk4 ();
} else {
funk5 ();
}
}
}
foo2.c
#include <stdio.h>
void funk2(){
printf("\nfunk2\n");
}
void funk3(){
printf("\nfunk3\n");
}
foo3.c
#include <stdio.h>
void funk4(){
printf("\nfunk4\n");
}
void funk5(){
printf("\nfunk5\n");
}
makefile
outputTest: foo1.o foo2.o foo3.o
gcc -o output foo1.o foo2.o foo3.o
make removeO
outputTest.o: foo1.c foo2.c foo3.c
gcc -c foo1.c foo2.c foo3.c
clean:
rm -f *.o output
removeO:
rm -f *.o
Since there's no mention of how to compile a .c file together with a bunch of .o files, and this comment asks for it:
where's the main.c in this answer? :/ if file1.c is the main, how do
you link it with other already compiled .o files? – Tom Brito Oct 12
'14 at 19:45
$ gcc main.c lib_obj1.o lib_obj2.o lib_objN.o -o x0rbin
Here, main.c is the C file with the main() function and the object files (*.o) are precompiled. GCC knows how to handle these together, and invokes the linker accordingly and results in a final executable, which in our case is x0rbin.
You will be able to use functions not defined in the main.c but using an extern reference to functions defined in the object files (*.o).
You can also link with .obj or other extensions if the object files have the correct format (such as COFF).
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