CXX - Rust和C ++之间的安全互操作

2021-03-26 20:11:37

此库提供了一种安全机制,用于从C ++调用来自C ++的RUST Andrust代码的C ++代码,而不会受到在BINGGEN或CBINDGEN生成不安全的C样式绑定的许多方面的许多方式。

这并不改变100%的C ++代码不安全的事实。审核Aproject时,您将在挂钩上审核所有不安全的生锈代码和所有C ++代码。在这个新模型下的核心安全索赔是,审计委员会C ++侧是足以捕获所有问题的核心,即防锈蛇形是100%安全的。

我们的想法是,我们在一个生锈模块中定义了我们的FFI两侧的签名,在一个生锈模块中(下一节显示了一个例子)。从此,CXX接收边界的完整图片,以执行静态analysagainst的类型和功能签名,以维护生锈' s和c ++' sinvariants和要求。

如果一切都静态检查,那么CXX使用一对代码生成器ToEmit相关的extern" c"两侧的签名以及稍后在构建过程中的任何静态断言到验证验证。在Rust侧,此代码生成器只是一个属性本质宏。在C ++方面,如果您的Build由Cargo管理,或者像Bazel或Buck Weprovide这样的其他构建系统,则可以是一个小型货物构建脚本,它是生成标题和源文件的命令行工具,并且易于集成。

由此产生的FFI桥在零或可忽略的开销中运行,即Nocopying,无序列化,无内存分配,无需运行时检查。

FFI签名能够使用它们的侧面,例如生锈' s字符串或c ++' s std :: string,生锈' s box或c ++&#39 ; s std :: unique_ptr,生锈' s vec或c ++' s std :: vector等.cxx保证双方理解的abi兼容的签名,基于onbuiltin的关键标准绑定库类型将惯用API公开到其他语言。例如,当在rust中操纵C ++ String时,其LEN()方法成为C ++函数函数的大小()成员的呼叫;从C ++操纵生锈字符串时,其大小()成员功能呼叫rust' s len()。

在此示例中,我们正在编写一个生锈应用程序,希望占据现有的C ++客户端用于大文件BlobStore服务。 BlobStoreUpports为一个不连续的缓冲区上载的操作。例如,WEMight正在上传循环缓冲区的快照,该循环缓冲区将倾向于组成2块块,或者文件的碎片以其他原因在内传播。

此示例的可抵消版本是在repo的demo目录下提供的。要尝试,请从该目录运行货物运行。

#[cxx :: bridge] mod ffi {//任何共享结构,其字段都可以看到两种语言。 struct blobmetadata {size:measize,标签:vec<字符串>,} extern"生锈" {//两个语言可以传递的零或更多不透明类型,但只能生锈可以看到字段。键入multibuf; //以锈病实施的函数。 fn next_chunk(buf:& mut multibuf) - > & [U8]; Unsafe extern" c ++" {//带有匹配的C ++声明的一个或多个标题。我们的代码//生成器Don' t读取它,但它得到#include' d,并用于静态//断言,以确保我们的FFI边界的图片是准确的。包括!("演示/包含/ blobstore.h"); //两种语言可以传递的零或更多不透明类型,但只能看到c ++。键入blobstoreclient; //在C ++中实现的函数。 fn new_blobstore_client() - > uniqueptr< blobstoreclient&gt ;; FN Put(& self,零件:& mut multibuf) - > U64; Fn标签(& self,blobid:u64,标签:& str); FN元数据(& self,blobid:u64) - > blobmetadata; }}

现在我们只是提供了外部的所有东西的生锈定义; rust" extern&#34中所有东西的块和c ++定义; c ++"块,安全地拨打电话。

通过CXX Codegenerators查看两种语言中生成的代码:

#运行生锈代码生成器并打印到stdout#运行 - 曼菲斯特 - 路径Gen / CMD / CARGO.TOML - DEMO / SRC / MAIN.RS

如示例所示,FFI边界的语言涉及3种ITEMS:

共享结构 - 他们的字段是对两种语言都可以看到的。CXX ::桥梁中写入的定义是单一的真理来源。

不透明类型 - 他们的字段是来自其他语言的秘密。这些不能按价值跨越FFI传递,而是仅在间接后面传递,例如参考&,铁锈箱或uniqueptr。可以是任意复杂的通用语言特定类型的典型典型类型,具体取决于您的使用情况。

在extern&#34内;生锈" CXX桥的一部分我们列出了生锈的类型和罚款是真理的根源。这些都隐含地引用了CXX桥的父模块的超级模块。您可以想到在上面的示例中列出的thetwo项目,如使用super :: multibuf,并使用super :: next_chunk除了重新导出到C ++。父模块将直接为简单的事物进行定义,或包含相关的使用语句,从其他地方带来它们的范围。

在extern" c ++"部分,我们列出了C ++的类型和函数,其中的真相源,以及声明这些API的标题。在upentit&#39中可能是从头开始的Bindgen样式,但现在我们需要写出的签名;静态断言愿意验证它们是准确的。

您的功能实现本身,无论是在C ++还是RURT中,都不需要被定义为extern" c" abi或no_mangle。 CXX将放入正确的下肢,使其成为所有工作。

请注意,使用CXX,重复所有函数签名:他们在CXX ::桥模块内部的实现(在C ++或RUST)和AndAgain中,虽然编译时断言保存,但它们被打开了一次。这与Bindgen和CBIndgen不同,其中函数签名由人类键入一次,该工具将它们消耗在内链内,并以其他语言发出它们。

这是因为CXX填补了一些不同的作用。这是一个较低水平的刀具Bindgen或Cbindgen的意义;您可以将其视为替代外部概念和#34; C"我们所知道的签名,而不是为BINDGEN进行释放。在CXX上构建一个更高的LevelBindgen的工具是合理的,该工具消耗C ++标题和/或restry模块(和/或idl等节奏)作为真值源,并生成CXX :: Bridge,消除了重复利用CXX的静态分析安全隐照。

但是在其他方式中,CXX的级别比绑定更高,具有富有的常用标准库类型。常常使用BINDGEN时,当我们处理惯用的C ++ API时,我们最终会在C-styleraw指针函数中手动包装,应用BINDGEN以获得不安全的原始指针RUSTFUNCTIONS,并再次复制API以在RUSTOMIALIALIALIALIALICALIAL上进行rust.that&#39 ; SA更糟糕的重复形式,因为它一直不安全。

通过使用CXX Bridge作为语言之间的共享了解,而是extern" C" C型签名作为共享了解,常见的FFI useecases使用100%安全代码变得表示。

混合和匹配也是合理的,使用CXX桥用于95%的FFI,这是简单的,并与Bindgen和CBIndgen一起做出剩余的几个古怪的签名,如果出于某种原因CXX' staticretress进入道路。如果您最终占据了这一问题,请提交一个问题,以便我们知道使工具莫尼克斯的工具是什么值得的。

对于由Cargo策划策划的构建,您将使用RunSCXX' S C ++代码生成器的构建脚本,并将生成的C ++代码与箱子的任何其他C ++代码一起编译。

规范构建脚本如下。指示的行返回CC :: Build实例(来自通常广泛使用的CC Crate),您可以在其中配置任何其他源文件和编译器标志正常。

// build.rs fn main(){cxx_build :: bridge(" src / main.rs")//返回cc :: build。文件(" src / demo.cc")。 flag_if_supported(" -std = c ++ 11")。编译(" cxxbridge-demo"); Println!("货物:重新运行= src / main.rs"); println!("货物:重新运行= src / demo.cc"); println!("货物:重新运行rerun-if-explated =包括/ demo.h");}

对于像Bazel或Buck这样的非货物构建,CXX提供了一种将C ++代码生成器作为独立命令行工具的替代方式。作为Cxxbridge-CMD Crate的工具被封装为Crate.IO或者可以从该回购的Gen / CMD目录构建。

请注意,这个图书馆的设计是故意限制的和疏货价!它不是一个足够强大的目标,以便以任何一种语言处理任意任务。相反,这个项目是关于雕刻出于其它能够做出有用的安全隐形的最具表现力的功能,并且可能随着时间的推移而延伸。您可能会发现它需要一些人使用CXX Bridge,因为它在您曾经使用过的所有方式工作。

通过设计,我们的配对代码生成器一起工作以控制FFI边界的两侧。通常,在锈的写作自己的extern" c"阻止ISUnsafe,因为Rust编译器无法知道签名是否' VE实际匹配在其他语言中实现的签名。通过CXX,我们实现了这种能见度,并了解了其他人的内容'

我们的静态分析检测并阻止通过V值得的值来通过C ++传递到生锈的价值,例如因为它们可能会被生锈&#39搞砸的Internal指针。

对于许多人来说,令人惊讶的是,可以在C ++中拥有Reart和Astruct的结构,具有完全相同的布局/字段/对齐/一切,并且在按值传递时仍然不相同的ABI。这是一个长期矛盾的错误,导致绝对正确的代码(Rust-lang / Rust-Bidgen#778)中的SegFaults。 CXX了解这一点,可以在需要的情况下透明地插入然后透明地插入零零成本解决方案,所以通过价值毫无疑问地持续到您的结构。这是通过边界的所有边缘而不是一个。

模板实例化:例如,为了暴露uniqueptr< t> TypeIN RUDE由真正的C ++ OUTIP_PTR备份,我们有一种使用RUST TRAITTO将行为连接到由其他语言执行的模板实例化的方法。

除了所有原始类型(I32< => int32_t)之外,可以在共享结构的字段和函数中的参数中使用以下多语载类型。

Rust命名空间的C ++ API由包含/ CXX.h文件Inthis Repo定义。当这些类型的工作时,您需要在C ++代码中包含此标题。

以下类型旨在支持"很快"但刚刚臭味。我不期待任何这些都是难以做好工作,而是在非母语中为每个人设计一个很好的API而努力工作。

这仍然是CXX的早期日子; 我正在将其释放为最低可行的产品,收集对方向和邀请合作者的反馈。 请检查各自的问题。 特别是如果您遇到麻烦构建或链接此内容,请报告问题。 我肯定有办法使构建方面更友好或更强大。 最后,我了解更多关于RUST图书馆设计的关于C ++图书馆设计,所以我会欣赏帮助在这个项目中制作C ++ API更加惯用的WHEREANYONE有建议。 在您的选项中的Apache许可证,版本2.0或MIT许可证中许可。 除非您明确说明,否则您在Apache-2.0许可证中定义的任何贡献,您将在本项目中纳入本项目,应在没有任何其他条款或条件的情况下进行双重许可。