使用 clang 与 C 链接时缺少 Rust 目标文件

标签 rust linker clang

我试图通过一些简单的 FFI 工作来弄清楚如何让 Rust 与 C 或 C++ 代码一起工作。我的直接问题是:

当我将 Rust 代码编译为目标文件时,它似乎缺少一些用于 clang 链接器以完成可执行文件的绑定(bind)。这是我正在使用的两个非常简单的文件。

clink.c

#include <stdio.h>
#include <stdbool.h>

int rust_int(int);
bool rust_bool(int, int);

/********************************/
/* void rust_char_star(char *); */
/********************************/

int main(void) {
    printf("%d\n", rust_int(5));
    if (rust_bool(5, 6)) {
        printf("True\n");
    }
    else { printf("False\n"); }

    /******************************/
    /* rust_char_star("Testing"); */
    /******************************/

    return 0;
}

ruSTLink.rs

use std::os::raw::{c_int, c_char};
// use std::ffi::{CStr, CString};

#[no_mangle]
pub extern "C" fn rust_int(i: c_int) -> c_int {
    i
}

#[no_mangle]
pub extern "C" fn rust_bool(x: c_int, y: c_int) -> bool {
    if x > y {
        true
    }
    else {
        false
    }
}

////////////////////////////////////////////////////////////////////////////
// #[no_mangle]                                                           //
// pub extern "C" fn rust_char_star(c: *const c_char) {                   //
//     let str_printable = unsafe {CString::from_raw(c as *mut c_char) }; //
//                                                                        //
//     println!("{:?}", str_printable);                                   //
// }                                                                      //
////////////////////////////////////////////////////////////////////////////

当我在没有 CString 的情况下编译它们时,一切都按预期工作。忽略警告。

~/dev/rust/learn/clink$ rustc --emit obj --crate-type staticlib rustlink.rs
warning: unused import: `c_char`
use std::os::raw::{c_int, c_char};
                          ^^^^^^
= note: #[warn(unused_imports)] on by default

~/dev/rust/learn/clink$ clang clink.c rustlink.o -o test
~/dev/rust/learn/clink$ ./test
5
False

当我取消注释所有代码以使用 CString 类型时,这就是我遗漏的地方。我不会在删除注释的情况下重新发布上面的代码。不过,这是输出结果。

~/dev/rust/learn/clink$ rustc --emit obj --crate-type staticlib rustlink.rs 
warning: unused import: `CStr`
 --> rustlink.rs:2:16
  |
2 | use std::ffi::{CStr, CString};
  |                ^^^^
  |
  = note: #[warn(unused_imports)] on by default

~/dev/rust/learn/clink$ clang clink.c rustlink.o -o test
rustlink.o: In function `alloc::alloc::dealloc':
rustlink.3a1fbbbh-cgu.0:(.text._ZN5alloc5alloc7dealloc17hca8aab9ecdf50cafE+0x43): undefined reference to `__rust_dealloc'
rustlink.o: In function `rust_char_star':
rustlink.3a1fbbbh-cgu.0:(.text.rust_char_star+0xa): undefined reference to `std::ffi::c_str::CString::from_raw'
rustlink.3a1fbbbh-cgu.0:(.text.rust_char_star+0x3a): undefined reference to `<std::ffi::c_str::CString as core::fmt::Debug>::fmt'
rustlink.3a1fbbbh-cgu.0:(.text.rust_char_star+0xa9): undefined reference to `std::io::stdio::_print'
rustlink.o:(.data.DW.ref.rust_eh_personality[DW.ref.rust_eh_personality]+0x0): undefined reference to `rust_eh_personality'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
~/dev/rust/learn/clink$ 

再进一步。我试图将 Rust 代码编译成一个静态库,以便输入到 clang 中。我不确定为什么,但这是结果。

~/dev/rust/learn/clink$ rustc --crate-type staticlib rustlink.rs 
warning: unused import: `CStr`
 --> rustlink.rs:2:16
  |
2 | use std::ffi::{CStr, CString};
  |                ^^^^
  |
  = note: #[warn(unused_imports)] on by default

~/dev/rust/learn/clink$ ls
clink.c  librustlink.a  rustlink.o  rustlink.rs
~/dev/rust/learn/clink$ clang clink.c -o test -lrustlink
/usr/bin/ld: cannot find -lrustlink
clang: error: linker command failed with exit code 1 (use -v to see invocation)
~/dev/rust/learn/clink$ clang clink.c -o test -L -lrustlink
/tmp/clink-357672.o: In function `main':
clink.c:(.text+0x15): undefined reference to `rust_int'
clink.c:(.text+0x3a): undefined reference to `rust_bool'
clink.c:(.text+0x83): undefined reference to `rust_char_star'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
~/dev/rust/learn/clink$

这是我有点困惑的地方。我以为当你告诉 rustc crate-type 是静态的,它包含所有依赖项时,但我想情况并非如此。

显然我错过了一步。我更希望 rustc 包含所有绑定(bind),这样我就可以使用一个简单的命令来生成可执行文件。

最佳答案

我缺少标准库绑定(bind)。经过更多的谷歌搜索后,我发现了一些有用的链接:

原来我只需要知道 Rust 标准目标文件在哪里。运行以下是一个解决方案:

~/dev/rust/learn/clink$ rustc --crate-type staticlib rustlink.rs
~/dev/rust/learn/clink$ clang clink.c -o test ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/libstd-89cf9eb8d404bb7b.so -L . -lrustlink
nick@Void:~/dev/rust/learn/clink$ ./test
5
False
"Testing"
Did I get here?
~/dev/rust/learn/clink$ 

我加了一行“Did I get here”是因为我本来编译成功的时候,程序还没完成就出现了segfault。我试图弄清楚是否是 CString 打印导致了它。原来我有一种双重自由状态。为了完整性,我将在最底部添加修改后的 Rust 代码。

作为第二种解决方案,更符合我的喜好,您可以使用目标文件:

~/dev/rust/learn/clink$ rustc --emit obj --crate-type staticlib rustlink.rs --verbose
~/dev/rust/learn/clink$ clang clink.c -o test rustlink.o ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/libstd-89cf9eb8d404bb7b.so 
~/dev/rust/learn/clink$ ./test
5
False
"Testing"
Did I get here?
~/dev/rust/learn/clink$ 

它们都产生相同的输出,我真的不能说哪一种方式比另一种更好。这取决于您在开发中的要求。

最后。我上面 ruSTLink.rs 的原始代码是错误的。我了解到使用绑定(bind)到原始指针的 CString 几乎不是一个好主意。我认为,在大多数情况下,它需要分配自己的内存。这是由于 drop。我将原始帖子中的 rust_char_star 函数更改为以下内容。

#[no_mangle]
pub extern "C" fn rust_char_star(c: *const c_char) {
    let ch = unsafe { CStr::from_ptr(c) };
    let str_printable = CString::from(ch);

    println!("{:?}", str_printable);
    println!("Did I get here?");
}

这可以有效防止双重释放。

关于使用 clang 与 C 链接时缺少 Rust 目标文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54057837/

相关文章:

c++ - VC++ 中的函数级链接是否有任何负面影响?

clang - 如何使用 LLVM 生成 SVE 向量

c++ - 不能在 C++ 文件中包含标准库

rust - 如何在 Rust 中获取向量中的最小值?

rust - 我应该使用&str或String来实现TryFrom

c++ - Apple Mach O 链接器

python - Mac OS下安装pygresql时出现clang错误

concurrency - 从其他多个任务中通知一个任务,无需额外的工作

generics - 我如何要求泛型类型支持数字运算?

c++ - 使用CodeBlocks编译64位DLL会导致链接器错误