I tried to write a simple D Program and use it to access a simple C library but there is unknown error.
My c Code, Box.c
#include "Box.h"
int SayHello( int _int)
{
_int ++;
return _int;
}
My c header file, Box.h
#ifndef BOX_H_INCLUDED
#define BOX_H_INCLUDED
/* export interfaces */
#ifdef __cplusplus
extern "C" {
#endif
int SayHello( int _int);
#ifdef __cplusplus
}
#endif
#endif // BOX_H_INCLUDED
I compile it
gcc -c Box.c Box.h
resulting files
Box.o
Box.h.gch
I place them to my D Program's project directory
My D Code
module main;
import std.stdio;
import std.conv;
import std.c.stdio;
import clib;
int main(string[] args)
{
// test external c library
auto s = to!string( SayHello(3) ) ;
writefln( "my int is "~ s );
readln();
return 0;
}
My D interface file ( clib ), trying to link to my C library
module clib;
import std.c.stdio;
extern (C) int SayHello( int _int);
The error I get when I compile it using codeblocks
Compiling: hello.d
Linking console executable: bin/Debug/tutorial03-access-c-library4
obj/Debug/hello.o: In function `_Dmain':
/home/hamilton/Tutorial/tutorial03-access-c-library4/hello.d:11: **undefined reference to `SayHello'**
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
0 errors, 0 warnings
Error is "undefined reference to `SayHello'"
There is no error I get when I compile it using command in console
$ dmd Box.o hello.d clib.di
it will be very painful if I cannot use codeblocks as I need the debugging functionality.
Thanks
Update:
Compiler setting in codeblocks as followed
Linker for dynamic libs: gcc -m32 -lrt
Linker for static libs: ar
Debugger: gdb
You can change the build options in CodeBlocks from Project -> Build Options, Compiler settings -> Other options. The simplest thing to do would be to just add Box.o to Other options.
Related
I am trying to port my simple application from C to Rust. It was running only on my Mac, with a library on Mac only. Here is a simplified version of the failed part in C code
// myLog.h
#include <os/log.h> // macOS header
void debug(const char *str);
//************************************
// myLog.c
#include "myLog.h"
void debug(const char* str) {
// call the macOS log function
os_log_debug(OS_LOG_DEFAULT, "%{public}s", str);
}
This code can be compiled simply calling gcc debug.c, and it works fine.
Then I added the .h and .c to my rust project with bindgen specified like below
fn main() {
println!("cargo:rerun-if-changed=myLog.h");
let bindings = bindgen::Builder::default()
.header("myLog.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to build bindgen");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("mylog_bindings.rs"))
.expect("Couldn't write bindings!");
}
And the main function has no other functions, but testing the log for now:
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use std::ffi::CString;
include!(concat!(env!("OUT_DIR"), "/mylog_bindings.rs"));
fn main() {
let log_infomation = CString::new("Log from Rust").expect("Failed to create c string");
let c_pointer = log_infomation.as_ptr();
unsafe {
debug(c_pointer);
}
}
The program failed with following error:
error: linking with `cc` failed: exit code: 1
|
= note: "cc" "-m64" "-arch" "x86_64" "-L" ......
= note: Undefined symbols for architecture x86_64:
"_debug", referenced from:
bindgen_test::main::hc0e5702b90adf92c in bindgen_test.3ccmhz8adio5obzw.rcgu.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: aborting due to previous error; 2 warnings emitted
error: could not compile `bindgen_test`.
I am not sure why this failed, but I found if I remove the whole unsafe block (without calling the function), the compilation will work. But can someone explain to me what I did wrong? Is there something I need to add to make it compile?
Thank you very much!
The problem is that you are not including the myLog.c file anywhere, only the myLog.h header. This is what bindgen does: it converts a C header file into Rust code, but it does not compile the C code itself.
For that you need the cc crate. You have to use both cc and bindgen together in your build.rs file:
use std::env;
use std::path::PathBuf;
fn main() {
println!("cargo:rerun-if-changed=myLog.h");
println!("cargo:rerun-if-changed=myLog.c"); // new line here!!
let bindings = bindgen::Builder::default()
.header("myLog.h")
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to build bindgen");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("mylog_bindings.rs"))
.expect("Couldn't write bindings!");
//Compile and link a static library named `myLog`:
cc::Build::new()
.file("myLog.c")
.compile("myLog");
}
And do not forget to add the cc crate to your build-dependencies.
I have some C code which I compiled to a .so file which i want to be called from a Rust program.
// hello.c
void greet() {
printf("Hello, world");
}
so I compiled it to a shared object file and added it to my build.rs and it worked fine
// main.rs
#[link(name = "hello")]
extern "C" {
fn greet();
}
fn main() {
unsafe {
greet();
}
}
The problem is the I have a second function in my C code which accepts a char* as a parameter so I tried to use libc::c_char to communicate between C and Rust but whenever my program doesn't compile when I import libc.
// main.rs
#[link(name = "hello")]
use libc::c_char;
extern "C" {
greet();
}
And I already tried to compile just with import libc (because I thought that might have been the problem) but it works perfectly so it seems like the program only doesn't compile when I am using my C shared object and importing the libc crate.
This is the error message
error: linking with `cc` failed: exit code: 1
= note: "cc"
= note: Undefined symbols for architecture x86_64:
"_greet", referenced from:
project::main::h501a37fa09c5db9f in project.2q2eogqn7p5k3u7s.rcgu.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The #[link] attribute must be just before the extern block. By inserting a use between the #[link] attribute and the extern block, the #[link] attribute becomes attached to the use and has no effect. (There really should be a warning for this...)
Works just fine for me, are you sure you compiled a static library that Rust linker can use regardless of what else gets linked into the final executable?
I can only guess that this is whats wrong as you haven't provided how exactly you setup your project, and I'd recommend letting cc crate handle it for you, and if you really need something it doesn't have, contribute to it, instead of manually compiling C code and trying to link it in.
Example
build.rs
fn main() {
cc::Build::new()
.file("src/hello.c")
.compile("hello");
}
src/hello.c
#include <stdio.h>
void greet() {
printf("Hello, world\n");
}
src/main.rs
use libc::c_char;
#[link(name = "hello")]
extern "C" {
fn greet();
}
fn main() {
unsafe {
greet();
}
}
cli
$ cargo run
Compiling link v0.1.0 (~/Desktop/link)
warning: unused import: `libc::c_char`
--> src/main.rs:4:5
|
4 | use libc::c_char;
| ^^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: 1 warning emitted
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
Running `~/.cargo/target/debug/link`
Hello, world
I am using Windows 7-64 bit.
Borland C++ 5.5 isn't working.
Test file Hello.c
#include<stdio.h>
int main(void)
{
printf("Hello world!\n");
return 0;
}
Error E2209 Hello.c 1: Unable to open include file 'stdio.h'
Warning W8065 Hello.c 5: Call to function 'printf' with no prototype in function main
* 1 errors in Compile *
I have already made the bcc32.cfg & ilink.cfg with these configs
bcc32.cfg
-I"c:\Borland\BCC55\include"
-L"c:\Borland\BCC55\lib"
ilink32.cfg
-L"c:\Borland\BCC55\lib"
:confused:
First look for your include and library directory and see whether it is the same as in your borland C directories option
I am a unskilled programmer and new to linux, I run into a problem when complining. I have two files 'ex_addinst.c' and 'lindo.h' in the same folder, I input command :
g++ -c ex_addinst.c
then, a object file ex_addinst.o is genetated with a warning:
ex_addinst.c: In function ‘int main()’:
ex_addinst.c:80: warning: deprecated conversion from string constant to ‘char*’
then I leak them with
g++ -Wall -o ex_addinst ex_addinst.o
and get the following info:
ex_addinst.o: In function `main':
ex_addinst.c:(.text+0x2b): undefined reference to `LSloadLicenseString'
ex_addinst.c:(.text+0x75): undefined reference to `LSgetVersionInfo'
ex_addinst.c:(.text+0xae): undefined reference to `LScreateEnv'
ex_addinst.c:(.text+0x10a): undefined reference to `LSgetErrorMessage'
...
...
ex_addinst.c:(.text+0x1163): undefined reference to `LSdeleteEnv'
collect2: ld returned 1 exit status
I guess that the header file 'lindo.h' is not complied into the .o file, but I have no idea what to do now. I have tried gcc, but get the same error. the version of my g++ and gcc is 4.4.5. I am using Ubuntu 10.10.
All the functions and structures have been declared in 'lindo.h'.
part of ex_addinst.c is as follows:
#include <stdio.h>
#include <stdlib.h>
/* LINDO API header file */
#include "lindo.h"
enter code here
int CALLTYPE LSwriteMPIFile(pLSmodel pModel,
char *pszFname);
/* Define a macro to declare variables for
error checking */
#define APIERRORSETUP \
int nErrorCode; \
char cErrorMessage[LS_MAX_ERROR_MESSAGE_LENGTH] \
/* Define a macro to do our error checking */
#define APIERRORCHECK \
if (nErrorCode) \
{ \
if ( pEnv) \
{ \
LSgetErrorMessage( pEnv, nErrorCode, \
cErrorMessage); \
printf("nErrorCode=%d: %s\n", nErrorCode, \
cErrorMessage); \
} else {\
printf( "Fatal Error\n"); \
} \
exit(1); \
} \
#define APIVERSION \
{\
char szVersion[255], szBuild[255];\
LSgetVersionInfo(szVersion,szBuild);\
printf("\nLINDO API Version %s built on %s\n",szVersion,szBuild);\
}\
/* main entry point */
int main()
{
APIERRORSETUP;
pLSenv pEnv;
pLSmodel pModel;
char MY_LICENSE_KEY[1024];
/*****************************************************************
* Step 1: Create a model in the environment.
*****************************************************************/
nErrorCode = LSloadLicenseString("home/li/work/tools/lindo/lindoapi/license/lndapi60.lic", MY_LICENSE_KEY);
if ( nErrorCode != LSERR_NO_ERROR)
{
printf( "Failed to load license key (error %d)\n",nErrorCode);
exit( 1);
}
......
......
......
APIERRORCHECK;
{
int nStatus;
double objval=0.0, primal[100];
/* Get the optimization result */
nErrorCode = LSgetInfo(pModel, LS_DINFO_GOP_OBJ, &objval);
APIERRORCHECK;
LSgetMIPPrimalSolution( pModel, primal) ;
APIERRORCHECK;
printf("\n\nObjective = %f \n",objval);
printf("x[0] = %f \n",primal[0]);
printf("x[1] = %f \n",primal[1]);
/* Get the linearity of the solved model */
nErrorCode = LSgetInfo (pModel, LS_IINFO_GOP_STATUS, &nStatus);
APIERRORCHECK;
/* Report the status of solution */
if (nStatus==LS_STATUS_OPTIMAL || nStatus==LS_STATUS_BASIC_OPTIMAL)
printf("\nSolution Status: Globally Optimal\n");
else if (nStatus==LS_STATUS_LOCAL_OPTIMAL)
printf("\nSolution Status: Locally Optimal\n\n");
else if (nStatus==LS_STATUS_INFEASIBLE)
printf("\nSolution Status: Infeasible\n\n");
}
/* >>> Step 7 <<< Delete the LINDO environment */
LSdeleteEnv(&pEnv);
/* Wait until user presses the Enter key */
printf("Press <Enter> ...");
getchar();
}
part of 'lindo.h' is:
/*********************************************************************
* Structure Creation and Deletion Routines (4) *
*********************************************************************/
pLSenv CALLTYPE LScreateEnv(int *pnErrorcode,
char *pszPassword);
pLSmodel CALLTYPE LScreateModel(pLSenv pEnv,
int *pnErrorcode);
int CALLTYPE LSdeleteEnv(pLSenv *pEnv);
int CALLTYPE LSdeleteModel(pLSmodel *pModel);
int CALLTYPE LSloadLicenseString(char *pszFname, char *pachLicense);
void CALLTYPE LSgetVersionInfo(char *pachVernum, char *pachBuildDate);
Thank you!
Thank you guys answering my problem. As you suggested, I need to link the library when complining. I have gotten the executable file with:
gcc -o ex_addinst ./ex_addinst.o -L/home/li/work/tools/lindo/lindoapi/bin/linux64 -m64 -llindo64 -lmosek64 -lconsub3 -lc -ldl -lm -lguide -lpthread -lsvml -limf -lirc
but there comes another problem when run the executable file ex_addinst: after run:
./ex_addinst
there comes:
./ex_addinst: error while loading shared libraries: liblindo64.so.6.0: cannot open shared object file: No such file or directory
The tricky thing is, liblindo64.so.6.0 is in the lib folder which contains:
libconsub3.so libirc.so liblindojni.so libmosek64.so.5.0 lindo.par
libguide.so liblindo64.so liblindojni.so.6.0.3 libsvml.so placeholder
libimf.so liblindo64.so.6.0 libmosek64.so lindoapivars.sh runlindo
I have created symbolic links between liblindo64.so.6.0 and liblindo64.so with
ln -sf liblindo64.so.6.0 liblindo64.so
but it doesn't help.
Can anyone tell me what is wrong here?
(I am not sure I should put this question in a new post, but I think currently it is better to follow the old one)
Ok, lindo.h contains the prototypes for those functions, but where are the functions actually defined? If they're in another C file you need to compile that one too, and link both the object files together.
If the functions are part of another static library, you need to tell the linker to link that library along with your object file.
If they're defined with a shared library, you can probably get g++ to still link to it at compile time, and take care of the library loading etc. Otherwise you'll need to load the library at runtime and reference the functions from the library. This Wikipedia article on dynamic loading of shared libraries contains some example code.
Try
g++ -Wall -o ex_addinst ex_addinst.c
instead of
g++ -Wall -o ex_addinst ex_addinst.o
You want to compile the .c file, not the .o file.
You need to tell gcc to link with the library or object file(s) that contain the LS... functions you're using. The header file tells the compiler how to call them, but the linker needs to know where to get the compiled code from.
undefined reference to ... is not a declaration problem. The compiler fails because it can't find symbols (objects) which are related to those declared functions.
In your case, you use the Limbo API, and include the header file, but you don't tell the compiler to link with the library : that's why it doesn't find symbols.
EDIT : I had forgotten the part when you say you're new to Linux. To link with the library, you need to use the -L/-l options of g++. man g++ is always a good read, and the Limbo's documentation should be, too.
I am trying to write a program that uses a C API. This API just provides a api.h header file, a .dll and a .lib.
It is using __stdcall exports.
I tried including the api.h and adding the .lib and .dll file, but I still have the following error to the libraries path, but I still have the following error:
**** Build of configuration Debug for project TestLibsp ****
**** Internal Builder is used for build ****
gcc -LC:\Users\nbarraille\workspace\TestLibsp\lib -oTestLibsp.exe src\main.o
src\main.o: In function `main':
C:\Users\nbarraille\workspace\TestLibsp\Debug/../src/main.c:83: undefined reference to `sp_session_create#8'
C:\Users\nbarraille\workspace\TestLibsp\Debug/../src/main.c:86: undefined reference to `sp_error_message#4'
C:\Users\nbarraille\workspace\TestLibsp\Debug/../src/main.c:92: undefined reference to `sp_session_login#12'
collect2: ld returned 1 exit status
Build error occurred, build is stopped
Time consumed: 721 ms
Here is my .c file:
#include "api.h"
int main(int argc, char **argv){
sp_session *sp;
sp_error err;
sp_session_config spconfig;
/* Create session */
sp_error err = sp_session_create(&spconfig, &sp);
}
And here is the definition of the function I am trying to call in the api.h
#ifndef SP_LIBEXPORT
#ifdef _WIN32
#define SP_LIBEXPORT(x) x __stdcall
#else
#define SP_LIBEXPORT(x) x
#endif
#endif
SP_LIBEXPORT(sp_error) sp_session_create(const sp_session_config *config, sp_session **sess);
Any idea?
Thanks!
You need to link against the library with gcc -l<libname> -L<libpath>. I guess you are using eclipse, so you can easily add the lib in Project -> Properties -> C/C++Build -> Settings -> GCC C++ Linker -> Library