corrosion-rs / corrosion

Marrying Rust and CMake - Easy Rust and C/C++ Integration!
https://corrosion-rs.github.io/corrosion/
MIT License
1.1k stars 106 forks source link

How to link together a Rust binary and a CMake C/C++ library? #575

Open bertulli opened 2 weeks ago

bertulli commented 2 weeks ago

Hi, thanks for your work. I am trying to use Corrosion to build a binary crate using a C/C++ CMake built library (the Azure IoTHub C SDK). What I don't understand (or perhaps I missed in the docs) is how should I link the two, as I currently can't do that using plain CMake, and I don't know if the problem is from Corrosion, CMake, cxx or rustc.

In practice, I noticed that linking using only CMake (doesn't matter if I use target_link_libraries or corrosion_link_libraries) will correctly set up the relevant -L and -l flags on the rustc command invocation, but apparently the libraries are not correctly read(?). At compile time I get errors like:

error: linking with `/usr/bin/cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/home/abertulli/.pyenv/shims:/home/abertulli/.local/bin:/home/abertulli/.nvm/versions/node/v20.17.0/bin:/home/abertulli/.cargo/bin:/home/abertulli/.cargo/bin:/opt/cross/bin:/home/abertulli/.juliaup/bin:/home/abertulli/.pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/windows/system32:/mnt/c/windows:/mnt/c/windows/System32/Wbem:/mnt/c/windows/System32/WindowsPowerShell/v1.0/:/mnt/c/windows/System32/OpenSSH/:/mnt/c/Program Files (x86)/gnupg/bin:/mnt/c/Program Files/PuTTY/:/mnt/c/Program Files/Git/cmd:/mnt/c/Program Files/RedHat/Podman/:/mnt/c/Program Files/usbipd-win/:/mnt/c/Program Files (x86)/ZeroTier/One/:/mnt/c/Program Files/Git/usr/bin/:/mnt/c/Program Files/Emacs/emacs-29.2/bin:/mnt/c/Users/a.bertulli/AppData/Local/Programs/MiKTeX/miktex/bin/x64/:/mnt/c/Program Files/LLVM/bin:/mnt/c/Users/a.bertulli/AppData/Local/Programs/Python/Python312/Scripts/:/mnt/c/Users/a.bertulli/AppData/Local/Programs/Python/Python312/:/mnt/c/Users/a.bertulli/AppData/Local/Programs/Python/Launcher/:/mnt/c/Users/a.bertulli/AppData/Local/Microsoft/WindowsApps:/snap/bin" VSLANG="1033" "/usr/bin/cc" "-m64" "/tmp/rustcllAZs2/symbols.o" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps/MWE-5a8e08bed0afdbad.017sl3x39dr7a95bncd3r82w4.rcgu.o" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps/MWE-5a8e08bed0afdbad.4ll2ayfvxhlg5kxlgkz364mhj.rcgu.o" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps/MWE-5a8e08bed0afdbad.8uj0wnos7fpj9ydhzoapubjwv.rcgu.o" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps/MWE-5a8e08bed0afdbad.91jol5to3wh2g27i7cvteo7pt.rcgu.o" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps/MWE-5a8e08bed0afdbad.bsws2j3qppql098beuowf12c6.rcgu.o" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps/MWE-5a8e08bed0afdbad.djipba13frqq1hdgtkmq4gcew.rcgu.o" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps/MWE-5a8e08bed0afdbad.6s7a6x30fm1zvcnhjwrvclf02.rcgu.o" "-Wl,--as-needed" "-L" "/home/abertulli/MWE/build/out/provisioning_client" "-L" "/home/abertulli/MWE/build/out/provisioning_client" "-L" "/home/abertulli/MWE/build/out/provisioning_client" "-L" "/home/abertulli/MWE/build/out/iothub_client" "-L" "/home/abertulli/MWE/build/out/iothub_client" "-L" "/home/abertulli/MWE/build/out/provisioning_client" "-L" "/home/abertulli/MWE/build/out/provisioning_client" "-L" "/home/abertulli/MWE/build/out/c-utility" "-L" "/home/abertulli/MWE/build/out/provisioning_client" "-L" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps" "-L" "/home/abertulli/MWE/build/./cargo/build/debug/deps" "-L" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/build/MWE-0fa78f78156583e5/out" "-L" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/build/cxx-cf2982a195ed61c7/out" "-L" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/build/link-cplusplus-c358e0250f6db76a/out" "-L" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bdynamic" "-lprov_http_transport" "-lprov_mqtt_transport" "-lprov_mqtt_ws_transport" "-liothub_client_mqtt_transport" "-liothub_client" "-lprov_device_ll_client" "-lprov_auth_client" "-laziotsharedutil" "-lhsm_security_client" "-Wl,-Bstatic" "-lMWE" "/home/abertulli/MWE/build/cargo/build/x86_64-unknown-linux-gnu/debug/deps/libcxx-df72601e456f0a7e.rlib" "/home/abertulli/MWE/build/cargo/build/x86_64-unknown-linux-gnu/debug/deps/liblink_cplusplus-23461b61ab6d6e4e.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-1c4b19562077c20d.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-85a631ebc91746e0.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libobject-fdace1a0b4cda412.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libmemchr-e5c28d21823e9a85.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libaddr2line-1e0edbcd516a8cce.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libgimli-77a1dc5e8fb357d6.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_demangle-8c9d2edb6dff139f.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_detect-ecadd85ae8bacc0c.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libhashbrown-67895a0c8dd8130b.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_alloc-5b4263e767961458.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libminiz_oxide-4f03d5a171522141.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libadler-9e4e8543de06315e.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-7fc51dfce9c057eb.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcfg_if-7ec98a9b1cc1792f.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-2f9b4333f6d32438.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-b6fe0262c36c500a.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-2a862c0b1c86f483.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-500f37ee5bcf0ffe.rlib" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-06dfbf1de02fde3b.rlib" "-Wl,-Bdynamic" "-lstdc++" "-lgcc_s" "-lutil" "-lrt" "-lpthread" "-lm" "-ldl" "-lc" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/abertulli/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/deps/MWE-5a8e08bed0afdbad" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now"
  = note: /usr/bin/ld: /home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/build/MWE-0fa78f78156583e5/out/libMWE.a(a4d806b327d0af20-main.rs.o): in function `cxxbridge1$IoTHub_Init':
          /home/abertulli/MWE/build/./cargo/build/x86_64-unknown-linux-gnu/debug/build/MWE-0fa78f78156583e5/out/cxxbridge/sources/MWE/src/main.rs.cc:6: undefined reference to `IoTHub_Init'
          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#rustc-link-lib)

Note that the function I'm calling (and for which I set up the interface using cxx) is plain C IoTHub_Init() in the library libiothub_client.a, built by CMake in directory MWE/build/out/iothub_client. I checked and it is present, and the symbol is exported when using nm. You can see it is present in the rustc invocation (or at least it seems to me), but I get the error. Whereas, if I include them by the build.rs script, the linking works, so I was wondering if the problem was on Corrosion side. What am I doing wrong? Thanks!


Context:

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

include(FetchContent)

FetchContent_Declare( Corrosion GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git

GIT_TAG v0.5 # Optionally specify a commit hash, version tag or branch here

) FetchContent_MakeAvailable(Corrosion)

##################### Azure C SDK ############################

Set Azure IoT SDK C settings

set(use_mqtt ON CACHE BOOL "Set mqtt on" FORCE ) set(skip_samples ON CACHE BOOL "Set slip_samples on" FORCE ) set(BUILD_TESTING OFF CACHE BOOL "Set BUILD_TESTING off" FORCE )

additional provisioning settings

set(use_prov_client ON) set(hsm_type_x509 ON)

Add Azure IoT SDK C

add_subdirectory(./azure-iot-sdk-c out)

compileAsC99()

Conditionally use the SDK trusted certs in the samples

if(${use_sample_trusted_cert}) add_definitions(-DSET_TRUSTED_CERT_IN_SAMPLES) include_directories(${PROJECT_SOURCE_DIR}/certs) set(iothub_c_files ${iothub_c_files} ${PROJECT_SOURCE_DIR}/certs/certs.c) endif()

include_directories(.)

################## main ############################

Import targets defined in a package or workspace manifest Cargo.toml file

corrosion_import_crate(MANIFEST_PATH Cargo.toml CRATES MWE)

target_include_directories(MWE INTERFACE ${UMOCK_C_INC_FOLDER}) target_include_directories(MWE INTERFACE ${MACRO_UTILS_INC_FOLDER}) target_include_directories(MWE INTERFACE ${IOTHUB_CLIENT_INC_FOLDER}) target_include_directories(MWE INTERFACE azure-iot-sdk-c/provisioning_client/inc) target_include_directories(MWE INTERFACE azure-iot-sdk-c/iothub_client/inc) target_include_directories(MWE INTERFACE ${SHARED_UTIL_INC_FOLDER}) target_include_directories(MWE INTERFACE ${CMAKE_CURRENT_LIST_DIR}/adapters) target_include_directories(MWE INTERFACE ${AZURE_SDK_PATH}/deps/umock-c/inc) target_include_directories(MWE INTERFACE ${AZURE_SDK_PATH}/c-utility/deps/azure-macro-utils-c/inc)

target_include_directories(MWE INTERFACE ${DEV_AUTH_MODULES_CLIENT_INC_FOLDER})

if (${use_http}) corrosion_link_libraries(MWE prov_http_transport) endif() if (${use_mqtt}) corrosion_link_libraries(MWE prov_mqtt_transport prov_mqtt_ws_transport iothub_client_mqtt_transport) endif()

corrosion_link_libraries(MWE iothub_client) corrosion_link_libraries(MWE prov_device_ll_client) corrosion_link_libraries(MWE prov_auth_client) corrosion_link_libraries(MWE aziotsharedutil) corrosion_link_libraries(MWE hsm_security_client)

* `build.rs`
```rust
fn main() {
    cxx_build::bridge("src/main.rs")
        .include("azure-iot-sdk-c/deps/azure-macro-utils-c/inc")
        .include("azure-iot-sdk-c/deps/umock-c/inc/")
        .include("azure-iot-sdk-c/iothub_client/inc")
        .compile("MWE");

    // without these it doesn't work
    // println!("cargo:rustc-link-search=native={}", "build/out/iothub_client");
    // println!("cargo:rustc-link-lib=static={}", "iothub_client");

}

[[bin]] name = "MWE"

[dependencies] cxx = "1.0" regex = "1.11.1"

[build-dependencies] cxx-build = "1.0"

* `src/main.rs`
```rust
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
    include!("MWE/azure-iot-sdk-c/iothub_client/inc/iothub.h");

    fn IoTHub_Init() -> i32;
    }
}

fn main() {
    ffi::IoTHub_Init();
}
Christian-Prather commented 1 week ago

I would also like to add I am facing a similar issue I believe. I receive similar errors from my cxx_build when I allow corrosion to dictate the rust linker. If I set NO_LINKER_OVERRIDE then everything builds but at runtime there are undefined symbols.

jschwe commented 1 week ago

corrosion provides the corrosion_add_cxxbridge() helper function for generating the cxx bindings. Any build.rs files are basically a black box for corrosion, so it can't really help if you use the build.rs approach. You can make it work, but ensuring that the link line on both sides (C++ and rust) is correct can be difficult.

bertulli commented 1 week ago

Thanks for your answer! There's something I don't understand then, I think.

jschwe commented 1 week ago

In other words, I can use it to create a Rust library and link it into a C++ executable, not the other way around.

No, its supposed to work both ways. Here is a very simple example / test project which links a cpp library into a rust binary: https://github.com/corrosion-rs/corrosion/tree/master/test/cxxbridge/cxxbridge_cpp2rust