为什么 Ada DLL 在通过 FFI 接口从 Rust 调用时会卡在 adainit 上?

Kaa*_*ann 6 windows dll ada ffi rust

开心案例

使用 Mingw 我在 Ada 中成功编译了一个最小的 hello world windows DLL 并通过 FFI 接口使用它:

package MY_FFI is
    procedure Hello_World
        with
            Export => True,
            Convention => C,
            External_Name => "hello_world";
end MY_FFI;

package body MY_FFI is
    procedure Hello_World is
    begin
        Ada.Text_IO.Put_Line("Hello world!");
    end Send_Request;
end MY_FFI;
Run Code Online (Sandbox Code Playgroud)
package MY_FFI is
    procedure Hello_World
        with
            Export => True,
            Convention => C,
            External_Name => "hello_world";
end MY_FFI;

package body MY_FFI is
    procedure Hello_World is
    begin
        Ada.Text_IO.Put_Line("Hello world!");
    end Send_Request;
end MY_FFI;
Run Code Online (Sandbox Code Playgroud)

结果是:

step 1
step 2
Hello world!
step 3
Run Code Online (Sandbox Code Playgroud)

真正的问题

当我的 Ada 库变得更加复杂并且需要遵循额外的系统 DLL 时:

  • libgnarl-7.dll

  • libgnat-7.dll

  • libgcc_s_seh-1.dll (mingw posix)

  • libwinpthreadthread-1.dll (mingw posix)

我无法准确地挑出需要那些额外 DLL 的代码,但是一旦代码变得复杂到需要这些 DLL 时,它就会停滞在my_ffiinit函数的锈迹中,仅输出:

step 1
Run Code Online (Sandbox Code Playgroud)

相同的代码在 x64 Linux 上运行正常。其他 Linux 平台(powerpc、arm64)的交叉编译也有效。

当我使用时Library_Auto_Init="true",wine64 输出:

0009:err:module:LdrInitializeThunk "libmy_ffi.dll" failed to initialize, aborting
0009:err:module:LdrInitializeThunk Initializing dlls for L"Z:\\test.exe" failed, status 20474343
Run Code Online (Sandbox Code Playgroud)

主要项目gpr:

with "/opt/libA/a_lib.gpr";
with "/opt/libB/b_lib.gpr";

library project MY_FFI is
  for Languages    use ("Ada");
  for Library_Name use "my_ffi";
  for Library_Kind use "dynamic";
  for Library_Standalone use "encapsulated";
  for Source_Dirs  use ("src/**");
  for Library_Interface use (
    "my_ffi",
  );

  type Target_Type is ("windows64", "windows32", "linux64", "armhf", "powerpc", "arm64");
  Target : Target_Type := external ("target", "linux64");
  for Library_Dir use "lib/" & external ("target", "linux64");
  for Object_Dir   use  "obj/" & external ("target", "linux64");
end MY_FFI;
Run Code Online (Sandbox Code Playgroud)

预编译共享库 A 的 gpr:

library project a_lib is
  for Languages    use ("Ada");
  for Library_Name use "a";
  for Library_Kind use "dynamic";
  for Source_Dirs  use ("src/**");

  type Target_Type is ("windows64", "windows32", "linux64", "armhf", "powerpc", "arm64");
  Target : Target_Type := external ("target", "linux64");
  for Library_Dir use "lib/" & external ("target", "linux64");
  for Object_Dir   use  "obj/" & external ("target", "linux64");

  package Naming is
      for Spec_Suffix ("ada") use ".ads";
      for Body_Suffix ("ada") use ".adb";
      for Separate_Suffix use ".adb";
      for Casing use "MixedCase";
      for Dot_Replacement use "-";
   end Naming;

  package Compiler is
    for Default_Switches ("ada") use ("-fPIC");
  end Compiler;

  for Externally_Built use "true";
end a_lib;
Run Code Online (Sandbox Code Playgroud)

从源代码编译的库 B 的 gpr:

library project b_lib is
   for Source_Dirs use ("src");
   for Library_Name use "b";
   for Library_Dir use "lib";
   for Object_Dir use "obj";
   for Languages use ("Ada");

   package Compiler is
      for Switches ("ada") use ("-O2", "-ffunction-sections", "-gnatQ", "-fdata-sections", "-fPIC", "-gnatf", "-gnatwa");
   end Compiler;

   package Builder is
      for Switches ("ada") use ("-j0", "-k", "-s");
   end Builder;

   type Target_Type is ("windows64", "windows32", "linux64", "armhf", "powerpc", "arm64");
   Target : Target_Type := external ("target", "linux64");
   for Library_Dir use "lib/" & external ("target", "linux64");
   for Object_Dir   use  "obj/" & external ("target", "linux64");
end b_lib;
Run Code Online (Sandbox Code Playgroud)

Kaa*_*ann 5

问题是for Library_Standalone use "encapsulated";在主项目配置文件中使用了,当用默认值替换这个时,standard项目编译失败,报错如下:

shared library project "my_ffi" cannot import static library project "b"
Run Code Online (Sandbox Code Playgroud)

一旦将行for Library_Kind use "relocatable";添加到库 B 项目配置中,编译错误就会消失并libmy_ffi.dll成功编译。更重要的是,生成的 DLL 在从 Rust 调用时按预期工作。

仍未得到解答的怪癖:

  • 为什么这对于 Linux 平台不是问题
  • 为什么在gprbuild编译项目时不强制执行或警告唯一的静态策略