4

I have been trying to compile a simple rust cdylib crate in windows and linking it with a simple c program. Despite all my efforts I fail to link the dll file.

Minimal example

First of all my rustc version is:

C:\Users\User> rustc --version
rustc 1.50.0 (cb75ad5db 2021-02-10)

I have a basic Cargo.toml which includes a cbindgen and sets the crate type:

[package]
name = "mycrate"
version = "0.1.0"
authors = ["PauMAVA <--REDACTED-->"]
edition = "2018"

[lib]
name = "mycrate"
crate-type = ["cdylib"]

[build-dependencies]
cbindgen = "0.18.0"

Then the lib.rs only declares a really simple extern hello world function:

#[no_mangle]
pub extern "C" fn test_fn() {
    println!("Hello world from Rust!")
}

Finally, I generate the header file via cdbindgen in build.rs:

extern crate cbindgen;

use std::env;
use std::path::Path;
use cbindgen::{Config, Builder};

fn main() {
    let crate_env = env::var("CARGO_MANIFEST_DIR").unwrap();
    let crate_path = Path::new(&crate_env);
    let config = Config::from_root_or_default(crate_path);
    Builder::new().with_crate(crate_path.to_str().unwrap())
        .with_config(config)
        .generate()
        .expect("Cannot generate header file!")
        .write_to_file("testprogram/headers/mycrate.h");
}

The generated header is the following:

/* Generated with cbindgen:0.18.0 */

#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

void test_fn(void);

The C code I have is the following simple program:

#include <stdio.h>
#include "headers/mycrate.h"

int main() {
    printf("Hello world from C!\n");
    test_fn();
}

Just to clarify, my file structure is the following:

testprogram
|-- headers
|    |-- mycrate.h
|
|-- main.c
|-- mycrate.dll

When I try to compile and link I get a linker error:

C:\Users\User\...\testprogram> gcc .\main.c -L.\mycrate.dll -o .\main
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: C:\Users\Pau\AppData\Local\Temp\cccQGbDk.o:main.c:(.text+0x1b): undefined reference to `test_fn'
collect2.exe: error: ld returned 1 exit status

Additional Information

The weird thing is that when compiling in WSL (Linux) I get a .so Linux library that just links correctly:

$ gcc main.c -L./mycrate.so -o main
$ ./main
Hello world from C!
Hello world from Rust!

What am I missing here? I guess this is merely a linking issue but I can't find the source of it. Any help is appreciated!

EDIT

I have also tried using an absolute path when linking. I am currently using MinGW.

EDIT 2

Also tried linking with the include library mycrate.dll.lib generated by cargo:

C:\Users\User\...\testprogram> gcc .\main.c -L.\mycrate.dll.lib -o .\main
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: C:\Users\Pau\AppData\Local\Temp\ccL0sH7B.o:main.c:(.text+0x1b): undefined reference to `test_fn'
collect2.exe: error: ld returned 1 exit status

The rustc --version --verbose output is:

C:\Users\User\...\testprogram> rustc --version --verbose
rustc 1.50.0 (cb75ad5db 2021-02-10)
binary: rustc
commit-hash: cb75ad5db02783e8b0222fee363c5f63f7e2cf5b
commit-date: 2021-02-10
host: x86_64-pc-windows-msvc
release: 1.50.0
PauMAVA
  • 1,163
  • 14
  • 23
  • Shouldn't the command be `gcc .\main.c -l.\mycrate.dll -o .\main`? [`-L` and `-l` are different](https://stackoverflow.com/questions/29468211/gcc-difference-between-l-and-l-option-and-how-to-provide-complete-path-to-a-l) – ForceBru Mar 10 '21 at 11:28
  • Tried with `-l` but I have the same error. If I'm not wrong `-L` is to specify the path of the lib while `-l` will search in the `PATH` for the library. Also tried using an absolute path with no luck. – PauMAVA Mar 10 '21 at 11:32
  • Do you use Rust with MinGW or MSVC? – frankenapps Mar 10 '21 at 11:56
  • I am compiling using `MinGW`. – PauMAVA Mar 10 '21 at 12:19
  • I see, that you compile your C program using MinGW, my question is what toolchain your Rust Compiler uses? You can check it using `rustc --version --verbose`. Maybe that's what you meant to say, but it does not quite sound like it. – frankenapps Mar 10 '21 at 12:42
  • 1
    On Linux programs are linked directly with the library (the `.so` file is used both at build time and at run time). On Windows the `.dll` file is only used at run time and you should link with an "import library" (with the `.lib` extension). – Jmb Mar 10 '21 at 13:05
  • I have edited the question with the output of `rustc --version --verbose` and it seems that rust is working with `MSVC`. I have also tried to link with the import library as suggested by @Jmb with no luck. – PauMAVA Mar 10 '21 at 17:29

1 Answers1

3

I think there are multiple problems here.

  1. You compile the library for a 64bit system (because Rust is x86_64), but you try to link it with 32bit MinGW
  2. Rust is using the MSVC toolchain and you try to compile the C program with MinGW
  3. I am not entirely sure about that, but I think you linked the mycrate.dll.lib incorrectly. According to that answer you should prefix it with l like so: -L -lmycrate.dll.lib

Obviously there are multiple ways to fix that. For example you can install Rust, so that it uses the MinGW toolchain as explained here.

Or you can use Visual Studio to compile your C code. That is what I did, because I did not want to bother with reinstalling Rust (Please correct me if am wrong, but I think there is no easy way to configure both MSVC and MinGW backends, so that one can switch easily between them in Rust).

The steps to compile and link with VisualStudio 2019 are as follows:

  1. Build the Rust project cargo build --release with your 64 bit Rust installation using MSVC
  2. Create a new Empty C++ project
  3. Add main.c and insert your code
  4. In the same directory where your solution file is placed put headers/mycrate.h
  5. Copy mycrate.dll and mycrate.dll.lib into the same directory where your Solution file is placed
  6. Right click the C++ project in VisualStudio and select Properties
  7. Select Configuration=Release and Platform=x64
  8. Add $(SolutionDir)\headers; to C/C++ -> General -> Additional Include Directories
  9. Add $(SolutionDir)mycrate.dll.lib; to Linker -> Input -> Additional Dependencies
  10. Apply all changes and close the properties pane
  11. Build the project in Release mode with the x64 platform

If you build the Rust project in debug mode (e.g. cargo build) you will have to do the same for Debug mode.

I know it is kind of a convoluted process, so let me know if I should make Repo that serves as a demo for that process...

As I said, if you like to build using MinGW, you will have to setup Rust differently as explained here.

frankenapps
  • 5,800
  • 6
  • 28
  • 69
  • 1
    Thank you very much! Worked like a charm. Making an example repo with an example project and this process explained in the README would be great as this information is nowhere to be found in the docs. Thank you again for your great response! – PauMAVA Mar 11 '21 at 10:12
  • 2
    Glad you got it working. I think the reason why this is not documented anywhere on the Rust side is because there are so many different build systems and they all work differently. Getting this to work requires some VS specific knowledge, but it really depends on which platform you use to communicate with the rust code... And then you need to make sure, that you compile the Rust code in a way that is compatible. – frankenapps Mar 11 '21 at 10:18