创建包含字符串静态C结构 [英] Creating a static C struct containing strings

查看:118
本文介绍了创建包含字符串静态C结构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图创造锈一个动态库,导出一个结构作为将通过的dlopen加载到C程序符号()。

不过,我也陷入了一些设计缺陷在结构中访问第二个字符串的时候,所以我做了一个小测试程序,试图找出我做错了。

这是锈code(test.rs),用rustc --crate型dylib test.rs编译:

 #[再版(C)]
酒馆结构PluginDesc {
    名称:放;静态海峡,
    版本:放大器;静态海峡,
    描述:放;静态STR
}
#[no_mangle]
酒馆静态PLUGIN_DESC:PluginDesc = {PluginDesc
    名称:测试插件\\ 0,
    版本:1.0 \\ 0,
    说明:测试锈插件\\ 0
};

和这里是试图加载库(test.c的)C程序,编译海合会test.c以-ldl -o考:

 的#include<&dlfcn.h中GT;
#包括LT&;&stdio.h中GT;
typedef结构{
    为const char *名称;
    为const char *版本;
    为const char *描述;
} plugin_desc;
INT主(INT ARGC,字符** argv的){
    void *的手柄;
    plugin_desc *说明;    处理=的dlopen(./ libtest.so,RTLD_LOCAL | RTLD_LAZY);
    如果(!手柄){
        的printf(无法执行dlopen:%S \\ n,dlerror获得());
        返回1;
    }    DESC =(plugin_desc *)的dlsym(手柄,PLUGIN_DESC);
    如果(!DESC){
        的printf(未能对dlsym:%S \\ n,dlerror获得());
        返回1;
    }    的printf(名称:%P \\ N,desc->名);
    的printf(版本:%P \\ N,desc->的版本);
    的printf(说明:%P \\ N,desc->说明);    返回0;
}

这是输出:

 名称:0x7fa59ef8d750
版本:位于0xC
说明:0x7fa59ef8d75c

正如你所看到的,desc-的地址>版本实际上是位于0xC(12),这是第一个字符串的长度。因此,它看起来像被压缩到库中的结构也包含内存地址后,字符串的长度。

我使用了错误的字符串类型吗?正如你所看到的,我不得不也使手动NULL终止字符串。我试图用CString的包装,但似乎并没有在这种情况下工作(静态项目都不允许有析构函数)。

我运行的是最新每晚锈病在Linux上:

  $ rustc --version
rustc 0.12.0- pre-夜间(f8426e2e2 2014年9月16日2时26分01秒+0000)


解决方案

片的布局(&放大器; [T] &安培; STR )是一个指针后跟的长度,通过的的std ::原始的切片结构模块。这就是为什么读版本字段从C code显示了名称字段的值的长度。 (但是请注意,这片的确切的内存布局不认为是稳定的,所以它可能在以后的版本中改变在任何情况下,你应该的的传递特定锈的数据类型到C;只有通过基本类型 - 包括原始指针 - 和类型与#[再版(C)]

注释。

编辑:不幸的是,似乎没有办法,现在做到这一点的生锈。有函数来得到片原始指针,但函数调用中不允许静态初始化。正如在评论sellibitze建议,你应该定义在C源文件中的变量。

I'm trying to create a dynamic library in Rust that exports a struct as a symbol that will be loaded into a C program via dlopen().

However, I'm was running into some segfaults when accessing the second string in the struct, so I made a small test program to try figure out what I'm doing wrong.

This is the Rust code (test.rs), compiled with "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"
};

and here is the C program that attempts to load the library (test.c), compiled with "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;
}

This is the output:

name: 0x7fa59ef8d750
version: 0xc
description: 0x7fa59ef8d75c

As you can see, the address of desc->version is actually 0xc (12), which is the length of the first string. So it looks like the struct that gets packed into the library also contains the string length after the memory address.

Am I using the wrong string type here? As you can see I had to also make the strings NULL terminated manually. I tried to use the CString wrapper but that does not seem to work in this case ("static items are not allowed to have destructors").

I'm running the latest Rust nightly on Linux:

$ rustc --version
rustc 0.12.0-pre-nightly (f8426e2e2 2014-09-16 02:26:01 +0000)

解决方案

The layout of a slice (&[T] or &str) is a pointer followed by a length, as documented by the Slice struct of the std::raw module. That's why reading the version field from your C code shows the length of the name field's value. (Note, however, that the exact memory layout of slices is not considered stable, so it might change in a later version. In any case, you should not pass Rust-specific data types to C; only pass primitive types – which includes raw pointers – and types annotated with #[repr(C)].)

EDIT: Unfortunately, there seems to be no way to do this in Rust for now. There are functions to get raw pointers from slices, but function calls are not allowed in static initializers. As suggested by sellibitze in the comments, you should define that variable in a C source file.

这篇关于创建包含字符串静态C结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆