<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>bigfish的网络博客 - FFI</title>
      <link>https://blog.bigfisharc.cn</link>
      <description>记录技术和生活</description>
      <generator>Zola</generator>
      <language>zh</language>
      <atom:link href="https://blog.bigfisharc.cn/tags/ffi/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 17 Oct 2026 23:05:00 +0800</lastBuildDate>
      <item>
          <title>Rust FFI 和 ABI 是什么关系？从一个字段写错的结构体说起</title>
          <pubDate>Sat, 17 Oct 2026 23:05:00 +0800</pubDate>
          <author>bigfish</author>
          <link>https://blog.bigfisharc.cn/blog/20261017-2305-rust-ffihe-abishi-shen-me-guan-xi/</link>
          <guid>https://blog.bigfisharc.cn/blog/20261017-2305-rust-ffihe-abishi-shen-me-guan-xi/</guid>
          <description xml:base="https://blog.bigfisharc.cn/blog/20261017-2305-rust-ffihe-abishi-shen-me-guan-xi/">&lt;p&gt;先看一个能正常跑起来、但结果是错的例子。&lt;/p&gt;
&lt;p&gt;插件侧编译成 &lt;code&gt;cdylib&lt;/code&gt;，导出两个函数。&lt;code&gt;cdylib&lt;/code&gt; 是供其他代码加载、调用的动态库产物，不要求调用方也是 Rust：&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;repr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;C&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt; struct&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; Point&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt;    pub&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; x&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt;    pub&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; y&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;no_mangle&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt; extern&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt; fn&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-6&quot;&gt; point_new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; y&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt; -&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; Point&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    Point&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; x&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; y&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;no_mangle&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt; extern&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt; fn&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-6&quot;&gt; add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; b&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt; -&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3&quot;&gt;    a&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt; +&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; b&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;宿主用 Windows 的 &lt;code&gt;LoadLibraryA&lt;/code&gt; / &lt;code&gt;GetProcAddress&lt;/code&gt; 加载库、查找函数。它自己也定义了一个 &lt;code&gt;Point&lt;/code&gt;，却把字段顺序写反了。下面节选类型声明和调用，&lt;code&gt;symbol_address&lt;/code&gt; 是已查到的函数地址；完整宿主源码见后文下载链接：&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;repr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;C&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; Point&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3&quot;&gt;    y&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-3&quot;&gt;    x&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-5 z-d-4&quot;&gt;//&lt;/span&gt;&lt;span class=&quot;z-l-5 z-d-4&quot;&gt; 从查到的地址构造函数指针；声明由宿主自己保证正确。&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; new_fn&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt; extern&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt; &amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-6&quot;&gt; fn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;i32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; i32&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt; -&amp;gt;&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt; Point&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt; unsafe&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;    std&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-8 z-d-10&quot;&gt;mem&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-6&quot;&gt;transmute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt;symbol_address&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-2 z-d-8&quot;&gt;let&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; p&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt; =&lt;/span&gt;&lt;span class=&quot;z-l-9 z-d-6&quot;&gt; new_fn&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-7 z-d-2&quot;&gt;1&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-7 z-d-2&quot;&gt; 2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-l-9 z-d-6&quot;&gt;println!&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;point_new(1,2) -&amp;gt; x=&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt; y=&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;z-l-12 z-d-5&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; p&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;.&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span class=&quot;z-l-3&quot;&gt; p&lt;/span&gt;&lt;span class=&quot;z-d-8&quot;&gt;.&lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 rustc 1.94.0、&lt;code&gt;x86_64-pc-windows-msvc&lt;/code&gt; 环境下，这个实验打印：&lt;/p&gt;
&lt;pre class=&quot;giallo z-l-code z-d-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;add(2,3) = 5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;point_new(1,2) -&amp;gt; x=2 y=1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没有编译错误、链接错误，也没有崩溃。插件返回的第一个整数是 &lt;code&gt;1&lt;/code&gt;，宿主却把它当作 &lt;code&gt;y&lt;/code&gt;；第二个整数是 &lt;code&gt;2&lt;/code&gt;，宿主把它当作 &lt;code&gt;x&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这个例子里，两个结构体的字段类型、大小和对齐恰好相同，交换字段名没有改变底层传递形状。错的是双方对这些字节的解释。它提醒我们：&lt;strong&gt;调用能完成，不等于接口约定一致。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;本文先解释这个 &lt;code&gt;Point&lt;/code&gt;，再看调用约定、符号、内存和失败处理。最后把它们对回上一篇《&lt;a href=&quot;https://blog.bigfisharc.cn/blog/20261015-0900-rpicha-jian-abijia-zai-ji-zhi/&quot;&gt;从一个 helloworld 动态库到 rpi package：ABI 插件是怎样被加载的&lt;/a&gt;》中的插件接口。不需要先读上一篇。&lt;/p&gt;</description>
      </item>
    </channel>
</rss>
