【问题标题】:How to place a C struct on the function stack in Rust?如何在 Rust 的函数堆栈上放置一个 C 结构?
【发布时间】:2020-05-15 09:09:49
【问题描述】:

我想转移以下C代码

HMAC_CTX context;
HMAC_CTX_init(&context);

进入 Rust。但是,虽然定义 extern 函数很容易,但在 Rust 中直接使用 C 结构似乎是不可能的。

extern "C" {
  use HMAC_CTX; // does not work!
  fn HMAC_CTX_init(ctx: *mut HMAC_CTX);
}

我知道我可以在 Rust 中定义一个占位符结构

struct HMAC_CTX;

...但是 的实例可能没有足够的空间容纳真正的 C 结构体。

let mut ctx = HMAC_CTX;
unsafe { HMAC_CTX_init(&mut ctx); }

有没有办法在不重新定义整个 Rust 结构的情况下解决这个问题?这将创建从外部代码到我的 Rust 项目的依赖关系,我想避免这种情况。

【问题讨论】:

  • 大多数人使用rust-bindgen。这能解决你的问题吗?
  • " 但是,虽然定义 extern 函数很容易,但在 Rust 中直接使用 C 结构似乎是不可能的。"当然。 extern 声明提供了要调用的函数的签名,但从根本上讲,它们并不相关。对于结构,签名是整个结构定义
  • “它可能会解决问题,但我想避免使用代码生成器。”那么您可以手动对其进行代码生成:复制/粘贴原件,将其标记为repr(C) 并修复类型和所有方面的差异。
  • 更好的方法一是使用现有的绑定(openssl-sys 或更高级别的 openssl crate),更好的方法二是使用 bindgen。我不知道更好的方法 3.
  • HMAC_CTX 是一个不透明的结构。它的布局和内容永远不会出现在客户端代码中。它只用作HMAC_CTX*,即指针。因此,您的客户端 (Rust) 代码不需要了解其内部结构。对于客户端代码,这只是一个指向 OpenSSL 控制的一些数据的指针。

标签: c struct rust


【解决方案1】:

使用 rust-bindgen 生成 Rust 绑定。如果放置在构建脚本中,它将生成相应 C 结构的 rust 版本并保持同步。

增加了构建过程的复杂性并增加了依赖。

【讨论】:

    【解决方案2】:

    手动创建一个 Rust 版本的 C 结构。

    #[repr(C)]
    pub struct HMAC_CTX {
      md: *mut EVP_MD,
      md_ctx: EVP_MD_CTX,
      i_ctx: EVP_MD_CTX,
      o_ctx: EVP_MD_CTX,
      key_length: c_uint,
      key: [c_uchar; 128],
    }
    

    这需要跟随 C 代码的变化并手动更新 Rust 结构。还可能需要定义进一步的结构。向被调用库的内部添加依赖项:-(

    【讨论】:

    • 如果您需要一种将 C 代码转换为(不安全)Rust 代码的快速方法作为起点,您可以使用C2Rust。这是c2rust 转译器的基于网络的前端。
    【解决方案3】:

    创建一个占位符结构。

    pub struct HMAC_CTX {
      _placeholder: [c_uchar; 256],
    }
    

    这个结构需要足够大以容纳 C 结构的所有内部结构。间接依赖 :-( 如果 C 结构体大小超过占位符大小,可能会导致不安全行为。

    【讨论】:

    • 该结构还必须具有与 C 结构相同的对齐方式。如果 Rust 中的对齐更严格,您可以在将指针从 C 传递到 Rust 时调用未定义的行为。如果 C 中的对齐更严格,您可以调用未定义的行为。
    猜你喜欢
    • 2012-06-10
    • 2017-01-30
    • 2011-04-20
    • 2017-07-19
    • 1970-01-01
    • 2015-10-12
    • 2023-03-27
    • 1970-01-01
    • 2017-05-27
    相关资源
    最近更新 更多