zig creates a C library but not usable by C - c

I'm able to get Zig to create a C library but when I attempt to use said library from a C program, it fails to find the definition of the included function.
My library definition:
const std = #import("std");
export fn removeAll(name: [*]const u8, len: u32) u32 {
const n: []const u8 = name[0..len];
std.fs.cwd().deleteTree(n) catch |err| {
return 1;
};
return 0;
}
test "basic remove functionality" {
}
build.zig
const Builder = #import("std").build.Builder;
pub fn build(b: *Builder) void {
const mode = b.standardReleaseOptions();
const lib = b.addStaticLibrary("removeall", "src/main.zig");
lib.setBuildMode(mode);
switch (mode) {
.Debug, .ReleaseSafe => lib.bundle_compiler_rt = true,
.ReleaseFast, .ReleaseSmall => lib.disable_stack_probing = true,
}
lib.force_pic = true;
lib.setOutputDir("build");
lib.install();
var main_tests = b.addTest("src/main.zig");
main_tests.setBuildMode(mode);
const test_step = b.step("test", "Run library tests");
test_step.dependOn(&main_tests.step);
}
zig build creates the build directory with the libremoveall.a static library.
My C program:
#include <stdio.h>
int removeAll(char *, int);
int main(int argc, char **argv)
{
removeAll("/tmp/mytest/abc", 15);
return 0;
}
When I attempt to include it in my C program, it get the following error:
gcc -o main build/libremoveall.a main.c
/usr/bin/ld: /tmp/cckS27fw.o: in function 'main':
main.c:(.text+0x20): undefined reference to 'removeAll'
Any ideas on what I'm doing wrong?
Thanks
EDIT
Thanks Paul R and stark, flipping the order worked. Can you help me understand why the order matter?

The linker searches a library for unresolved references encountered so far in the process.
If you put your program after the library, there are no unresolved references when the linker reads the library. And then when it reads your program, there is no library to resolve the reference.
Swap your program and the library, and you are all set.

Related

How to link a C library in Rust WASI

I want to use a C library in my rust-wasi program. but I am having trouble linking external libraries. My Current setup is this.
main.rs
#[link(name = "mylib")]
extern "C" {
pub fn add_one(i: i32) -> i32;
}
pub fn main() {
let res = unsafe { add_one(10) };
println!("I + 1: {}", res);
}
https://github.com/codeplea/tinyexpr
mylib.cpp
#include "tinyexpr.h"
extern "C" int add_one(int i)
{
te_interp("i + 1", 0);
return i + 1;
}
build.rs
fn main() {
cc::Build::new()
.archiver("llvm-ar")
.cpp_link_stdlib(None)
.cpp(true)
.flag("--sysroot=/opt/wasi-sysroot/")
.file("mylib.cpp")
.compile("libmylib.a");
}
leading to this error when I try to execute it with wasmtime.
cargo build --target wasm32-wasi --release
wasmtime --dir=. --dir=/tmp target/wasm32-wasi/release/reverser.wasm
Error: failed to run main module `target/wasm32-wasi/release/so.wasm`
Caused by:
0: failed to instantiate "target/wasm32-wasi/release/so.wasm"
1: unknown import: `env::te_interp` has not been defined
I don't have any issues linking to the headers in the sys-root directory. Just with c headers in the same directory
tinyexpr is not a header-only library, you also need to compile tinyexpr.c:
cc::Build::new()
.archiver("llvm-ar")
.flag(&sysroot)
.file("tinyexpr.c")
.compile("tinyexpr");
though you don't necessarily need to give it its own library, you could also compile tinyexpr.c and mylib.cpp into the same .a. From what I understand about C/C++ build processes, that should give you the same result.
If you wanted to be real pretty about it, you'd make a new tinyexpr-sys crate that contains just tinyexpr.c (plus a cbindgen-generated lib.rs).
Side note: For finding the sysroot, I'd go with something like
let sysroot = var("MYLIB_WASI_SYSROOT")
.or(var("WASI_SYSROOT"))
.ok()
.or_else(|| Some(format!("{}/share/wasi-sysroot", var("WASI_SDK_PATH").ok()?)));
let sysroot = match sysroot {
Some(sysroot) => format!("--sysroot={}", sysroot),
None => {
eprintln!(
"Install wasi-sdk or wasi-libc and specify WASI_SYSROOT path in environment!"
);
exit(1);
}
};
though you could also just expect people to set CFLAGS/CXXFLAGS.
Other side notes:
You might have less of a headache with the rust version of this library
Related answer

Rust bindgen cannot find platform specific library?

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.

Why does use libc stop cargo from linking my program correctly?

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

how to read image from particular location using MEX?

I have a function written in C to read an image as follows:
Image *read_Image(char *filename, int showmessages)
Now I want execute this function in MATLAB by creating a gateway function and wrapper function in MEX. I have never wrote Matlab C/Mex code. I tried my luck after going through [http://cnx.org/contents/15601bc4-3cda-4964-a7e4-5e061c8aa8b7#2/Writing-C-Functions-in-MATLAB-][1] and wrote the following code. I still need to do a lot, i am stuck in midway. Can anyone guide me???
following are the code I wrote:
#include "mex.h"
#include "CVIPtools.h"
#include "CVIPimage.h"
#include "CVIPdef.h"
#include "CVIPmap.h"
#include "limits.h"
#include "threshold.h"
#include <float.h>
#include "CVIPmatrix.h"
//Here I will write a wrapper function.
/* main gateway function*/
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
mxArray *fnameData;
int fnameLength;
char *str;
//mxArray *smData;
int sm;
double *outArray;
int r,c,bands,n;
const mwSize *dim_array;
fnameData = prhs[0];
//smData = prhs[1];
fnameLength = mxGetN(fnameData)+1;
str = mxCalloc(fnameLength, sizeof(char));
mxGetString(fnameData, str, fnameLength);
// sm = (int)(mxGetScaler(smData));
sm = mxGetScaler(prhs[1]);
if (nrhs != 2) {
mexErrMsgIdAndTxt( "MATLAB:mxisfinite:invalidNumInputs",
"Two input arguments required.");}
if (nlhs != 1) {
mexErrMsgIdAndTxt( "MATLAB:mxisfinite:invalidNumInputs",
"One output argument required.");}
n = mxGetNumberOfElements(plhs[0]);
dim_array = mxGetDimensions(plhs[0]);
r = dim_array[0];
c = dim_array[1];
bands = dim_array[2];
if(bands==3){
plhs[0] = mxCreateNumericArray(3, dim_array, mxDOUBLE_CLASS, mxREAL);
}
else
{ plhs[0] = mxCreateDoubleMatrix(r,c,mxREAL);
bands=1;
}
outArray = mxGetData(plhs[0]);
// Here I will call wrapper function.
}
I just tried compiling this code using mex filename.c I got the following error.
mex image_readCVIP.c
Creating library D:\Users\Deepen\AppData\Local\Temp\mex_sedh1H\templib.x and object D:\Users\Deepen\AppData\Local\Temp\mex_sedh1H\templib.exp
image_readCVIP.obj : error LNK2019: unresolved external symbol mxGetScaler referenced in function mexFunction
image_readCVIP.mexw64 : fatal error LNK1120: 1 unresolved externals
D:\PROGRA~1\MATLAB\R2012A\BIN\MEX.PL: Error: Link of 'image_readCVIP.mexw64' failed
[1]: http://cnx.org/contents/15601bc4-3cda-4964-a7e4-5e061c8aa8b7#2/Writing-C-Functions-in-MATLAB-
I'm not aware of a function called mxGetScaler. Try mxGetScalar. If you really mean mxGetScaler, it must be from another library (your CVIP tools?). You will have to link any pre-compiled dependencies by appending the .lib files to the mex command, or compile their source along with the mex file by appending the source files to the command.
Note that you will have several other compilation issues with the shown code. const correctness and pointer casting errors abound.

Windows RPC programming error: LNK 2019 unresolved external symbol RPCServerListen

I am learning C Windows RPC programming. Here is the source code for a dummy RPC Server I wrote and compiled without errors:
#include <stdio.h>
#include "md5_h.h"
#include "rpc.h"
#include "rpcndr.h"
int main() {
RPC_STATUS status;
status = RpcServerUseProtseqEp(
(RPC_WSTR)("ncacn_ip_tcp"),
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
(RPC_WSTR)("9191"),
NULL);
if (status) { exit(status); }
status = RpcServerRegisterIf(
md5_v1_0_c_ifspec,
NULL,
NULL);
if (status) { exit(status); }
status = RpcServerListen(
1,
RPC_C_LISTEN_MAX_CALLS_DEFAULT,
FALSE);
if (status) { exit(status); }
return 0;
}
void __RPC_USER midl_user_free(void* p) {
free(p);
}
void md5(const unsigned char* szMsg) {
printf("voila %s\n", szMsg);
}
The midl files get compiled without errors as well. The MIDL-compilation produces md5_s.c and md5_c.c as expected. Here is md5.idl file if needed:
[
uuid(D86FBC01-D6A7-4941-9243-07A4EC65E8CB),
version(1.0),
]
interface md5
{
void md5([in, string] const char* szMsg);
};
During the Linkage stage the following errors are produced:
LNK2019: unresolved external symbol __imp__RpcServerListen referenced in function main
I have same errors for every RPC-specific functions, such as RpcServerRegisterIf or RpcServerUseProtseqEp. I am using Microsoft Visual Studio 2013.
I think this comes from a missing include; but I can't figure which one. I tried to include rpc.h, without any change.
Do I have to include in my project the produced md5_s.c? I have tried so without resolving anything.
Thanks for helping!
You need to link against Rpcrt4.lib.
If you are using visual studio, add it in the Project -> Properties -> Configuration Properties -> Linker -> Input -> Additional Dependencies.

Resources