我正在尝试在Rust中创建一个动态库,它将一个struct导出为一个符号,该符号将通过dlopen()加载到C程序中。
但是,在访问struct中的第二个字符串时,我遇到了一些段错误,所以我做了一个小测试程序,试着弄清楚我做错了什么。
这是Rust代码(test.rs),使用“rustc --crate-type dylib test.rs”编译:
#[repr(C)]
pub struct PluginDesc {
name: &'static str,
version: &'static str,
description: &'static str
}
#[no_mangle]
pub static PLUGIN_DESC: PluginDesc = PluginDesc {
name: "Test Plugin\0",
version: "1.0\0",
description: "Test Rust Plugin\0"
};
以下是尝试加载库(test.c)的C程序,使用“gcc test.c -ldl -o test”编译:
#include <dlfcn.h>
#include <stdio.h>
typedef struct {
const char *name;
const char *version;
const char *description;
} plugin_desc;
int main(int argc, char **argv) {
void *handle;
plugin_desc *desc;
handle = dlopen("./libtest.so", RTLD_LOCAL | RTLD_LAZY);
if (!handle) {
printf("failed to dlopen: %s\n", dlerror());
return 1;
}
desc = (plugin_desc *) dlsym(handle, "PLUGIN_DESC");
if (!desc) {
printf("failed to dlsym: %s\n", dlerror());
return 1;
}
printf("name: %p\n", desc->name);
printf("version: %p\n", desc->version);
printf("description: %p\n", desc->description);
return 0;
}
这是输出:
name: 0x7fa59ef8d750
version: 0xc
description: 0x7fa59ef8d75c
如您所见,desc-&gt;版本的地址实际上是0xc(12),这是第一个字符串的长度。因此,看起来打包到库中的结构也包含内存地址之后的字符串长度。
我在这里使用错误的字符串类型吗?正如您所看到的,我还必须手动将字符串NULL终止。我尝试使用CString包装器,但在这种情况下似乎不起作用(“静态项目不允许有析构函数”)。
我在Linux上运行最新的Rust:
$ rustc --version
rustc 0.12.0-pre-nightly (f8426e2e2 2014-09-16 02:26:01 +0000)
答案 0 :(得分:3)
简短的回答是你不能静态地分配这样的结构。 Future Rust可能会获得这种能力。
您可以做的是静态分配包含空指针的结构,并在调用函数时将这些空指针设置为有用的东西。 Rust有static mut
。它需要不安全的代码,不是线程安全,并且(据我所知)被视为代码异味。
就在这里,我认为这是一个解决方法,因为无法在静态中将&[T]
转换为*const T
。
static S: &'static [u8] = b"http://example.org/eg-amp_rust\n\0";
static mut desc: LV2Descriptor = LV2Descriptor {
amp_uri: 0 as *const libc::c_char, // ptr::null() isn't const fn (yet)
};
#[no_mangle]
pub extern fn lv2_descriptor(index: i32) -> *const LV2Descriptor {
let ptr = S.as_ptr() as *const libc::c_char;
unsafe {
desc.amp_uri = ptr;
&desc as *const LV2Descriptor
}
}
答案 1 :(得分:2)
切片(&[T]
或&str
)的布局是一个指针,后跟一个长度,如the Slice
struct of the std::raw
module所述。这就是为什么从C代码中读取version
字段会显示name
字段值的长度。 (但请注意,切片的确切内存布局不被认为是稳定的,因此在以后的版本中可能会更改。在任何情况下,您都应该不将特定于Rust的数据类型传递给C;仅传递原始类型 - 包括原始指针 - 和用#[repr(C)]
注释的类型。)
编辑:不幸的是,现在似乎没有办法在Rust中做到这一点。有些函数可以从切片中获取原始指针,但function calls are not allowed in static initializers。正如评论中sellibitze所建议的那样,您应该在C源文件中定义该变量。