rust-lang / cc-rs

Rust library for build scripts to compile C/C++ code into a Rust library
https://docs.rs/cc
Apache License 2.0
1.85k stars 443 forks source link

error LNK2019: unresolved external symbol `fn_name` referenced in function #891

Closed David-OConnor closed 12 months ago

David-OConnor commented 1 year ago

Hi! I'm attempting to use cc for FFI with a C++/CUDA program. I receive this error:

error: linking with `link.exe` failed: exit code: 1120
  |
  = note: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC\\14.37.32822\\bin\\HostX64\\x64\\link.exe" "/NOLOGO" "
  // ... lots of characters
    = note: wf_lab.wf_lab.49f2ab786cc9a047-cgu.10.rcgu.o : error LNK2019: unresolved external symbol ffi_test referenced in function _ZN6wf_lab4main17hc80f789cc725edc4E
          C:\Users\the_a\code\wf-lab\target\release\deps\wf_lab.exe : fatal error LNK1120: 1 unresolved externals

warning: `wf_lab` (bin "wf_lab") generated 61 warnings (32 duplicates)
The following warnings were emitted during compilation:

warning: cuda.cu
warning: cuda.cu

Cargo.TOML:

[build-dependencies]
# For CUDA FFI
cc = { version = "^1.0.83", features = ["parallel"] }

main.rs:

extern "C" {
    fn ffi_test();
}

fn test_cuda_ffi() {
    unsafe {
        ffi_test()
    }
}

fn main() {
    test_cuda_ffi();
}

Builds.rs:

use cc;

fn main() {
    cc::Build::new()
        .cuda(true)
        .cudart("static")
        // todo: Is expicitly specifying gencode required?
        // Generate code for RTX 2 series.
        .flag("-gencode").flag("arch=compute_75,code=sm_75")
        // Generate code in parallel
        .flag("-t0")
        .file("./cuda/cuda.cu")
        .compile("cuda_test");
}

cuda.cu:

// This C++ function can be called from C code
extern "C" void ffi_test() {
// void ffi_test() {
    std::cout << "FFI TEST" << std::endl;
}

Note: I think I have confirmation the C++ code is compiling correctly: If I put in random characters in the C++ function being called, I get an appropriate syntax error when I do cargo build.

Where would you start? TY!

dot-asm commented 1 year ago

Where would you start?

cargo clean.

David-OConnor commented 1 year ago

Where would you start?

cargo clean.

Set-Location: A positional parameter cannot be found that accepts argument 'clean'.

Of note, the associated error if compiling on Linux:

lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/mnt/c/Users/the_a/code/wf-lab/target/debug/deps/wf_lab-a1d82c572833d8e1" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs"
  = note: /usr/bin/ld: /mnt/c/Users/the_a/code/wf-lab/target/debug/deps/wf_lab-a1d82c572833d8e1.1717mp8s0eatta04.rcgu.o: in function `wf_lab::test_cuda_ffi':
          /mnt/c/Users/the_a/code/wf-lab/src/main.rs:286: undefined reference to `ffi_test'
          collect2: error: ld returned 1 exit status

  = note: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)

warning: `wf_lab` (bin "wf_lab") generated 62 warnings (32 duplicates)
dot-asm commented 1 year ago

cargo clean.

Set-Location: A positional parameter cannot be found that accepts argument 'clean'.

This is not an error message from cargo. Looks like from powershell? Come on...

David-OConnor commented 1 year ago

Looks like I had a typo. cargo clean runs, but no change in behavior.

dot-asm commented 1 year ago

no change in behavior.

Really? So it doesn't complain about not being able to compile the presented cuda.cu and actually reaches the linking stage? Just in case for reference, after fixing up cuda.cu I can't reproduce any of the referred problems. [Apart from cargo warnings on Windows, but those are benign and a different story.] Here is a test. Since you have no actual CUDA code, rename cuda.cu to cuda.cpp and omit CUDA flags/options in build.rs...

David-OConnor commented 1 year ago

I appreciate you trying it! I have been working that angle recently; here's the latest build.rs main: (same error)

fn main() {
    // Tell Cargo that if the given file changes, to rerun this build script.
    println!("cargo:rerun-if-changed=cuda/cuda.c");
    cc::Build::new()
        // .cpp(true)
        // .cuda(true)
        // .cudart("shared") // todo: troubleshooting.
        // Generate code for RTX 2 series.
        // .flag("-gencode").flag("arch=compute_75,code=sm_75")
        // Generate code in parallel // todo: Do we want this?
        // .flag("-t0")

        //         .include("...")
        //         .flag("-Llibrary_path")
        //         .flag("-llibrary")
        //         .compile("...");

        .file("cuda/cuda.c")
        .compile("cuda");
dot-asm commented 1 year ago

Something is seriously off with your setup. In other words it doesn't looks like cc-rs is at fault here. It looks as if C compiler is not called at all, or that it produces empty object files. See what's going on with cargo build -vvv (after cargo clean) in search for clues. But those would be for you. You should see cl.exe called. Note the object names and examine them with dumpbin /symbols... Try to calls cl yourself...

David-OConnor commented 12 months ago

I will post back here once I learn more. Currently unable to get cc to generate any files on Win or Linux. Very long linker errors. Compiling with the clang/clang++/nvcc CLI tools works. Open to ideas on how to streamline the process and make it just work, like normal rust code. This problem is ranking extreme on the difficulty and inscrutability scales.

David-OConnor commented 12 months ago

The problem was I was missing CXX = clang++ in the system environmental variables.

dot-asm commented 12 months ago

The problem was I was missing CXX = clang++ in the system environmental variables.

This doesn't really make sense, but if it helps you, then it helps you. But just in case for reference. The issue starts as "CUDA on Windows doesn't link." As far as I recall, using C++ compiler other than Visual Studio with CUDA on Windows is unsupported by Nvidia. This is unlike Linux, where it's perfectly fine to use clang++ with nvcc. Secondly, cc-rs is perfectly capable of working without any additional environment variables set. The fact that you have to set some is rather an anomaly than a rule, at least when compiling hello-ffi-style snippets. But again, if it works for you, then it works for you...

David-OConnor commented 12 months ago

Thank you for the input. Currently, using cc with (non-CUDA) C++ works, after setting that env var. It does not work without setting that. I'm having reasonably productive troubleshooting now with NVCC and CUDA using cc. For example I had this error with CC, cl : Command line error D8021 : invalid numeric argument '/Wextra', but was able to proceed past it, to a different error nvcc fatal : A single input file is required for a non-link phase when an outputfile is specified after copy+pasting the CC command output into a terminal directly, without that flag.

Once done, I will PR this repo with a self-contained example anyone can run.

I see what you're saying about the env variable nominally not being required. I have that in my Bayesian priors, but the evidence is pointing towards it not being the case.

dot-asm commented 12 months ago

There is a [CUDA] sanity test in cc-test. It's exercised on Github Action only on Linux though. The reason for why it's not exercised on Windows is because there is no scriptable installer (to perform minimal compiler installation). But one can execute it "manually" in cc-rs tree on a Windows system with working CUDA installation. As per .github/workflows/main.yml, with cargo test --manifest-path cc-test/Cargo.toml --features test_cuda.

David-OConnor commented 12 months ago

Excellent; thank you for that tip! That sounds great.