Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
bertulli opened this issue Nov 11, 2024 · 4 comments
Open

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

bertulli opened this issue Nov 11, 2024 · 4 comments

Comments

@bertulli
Copy link

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:

  • CMakeLists.txt
cmake_minimum_required (VERSION 3.18)
PROJECT(corrosion-test)

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
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");

}
  • the Azure submodule has been populated by Git in azure-iot-sdk-c
  • Cargo.toml
[package]
name = "MWE"
version = "0.1.0"
edition = "2021"
authors = ["Alessandro Bertulli <[email protected]>"]

[[bin]]
name = "MWE"

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

[build-dependencies]
cxx-build = "1.0"
  • src/main.rs
#![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
Copy link

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
Copy link
Collaborator

jschwe commented Nov 13, 2024

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
Copy link
Author

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

  • First: if I got it right, the reason the CMake linker isn't working in my example is that I'm trying to link the Azure library to my Rust executable, but the function throwing an error isn't there: it is function cxxbridge1$IoTHub_Init, which is generated by cxx and is found in libMWE.a. So I would need to link the Azure SDK to that, but this is not what I expressed in my current CMake. My first question is: should it really matter? Given that libMWE.a is a static library, can't I link it partially with the functions it finds, and then postpone all the "executable-level" linking just to the last invocation, where the executable is produced?

  • Second: how can I use Corrosion to do that? From what I understand from the docs, the function corrosion_add_cxxbridge is used to export the bindings to C++, not from. In other words, I can use it to create a Rust library and link it into a C++ executable, not the other way around. Is this correct, or am I doing something wrong? I tried adding to my example

corrosion_add_cxxbridge(iothub_client_cxx
  CRATE MWE
  FILES main.rs)
target_link_libraries(iothub_client_cxx iothub_client)

but it didn't work, also because I then can't find the headers in the cxx-bridge include. Thanks!

@jschwe
Copy link
Collaborator

jschwe commented Nov 14, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants