Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Ada DLL get stuck at adainit when called from Rust over FFI interface?

Happy case

Using Mingw I have successfully compiled a minimal hello world windows DLL in Ada and used it over the FFI interface:

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;
#[link(name = "my_ffi")]
extern "C" {
    #[link_name = "hello_world"]
    fn ada_hello_world();
    fn my_ffiinit(); // same as adainit just renamed by gprbuild
    fn my_ffifinal(); // same as adafinal just renamed by gprbuild
}

pub fn initialize_my_ffi() {
    unsafe {
        println!("step 1");
        my_ffiinit();
        println!("step 2");
        ada_hello_world();
        println!("step 3");
    }
}

Which results in:

step 1
step 2
Hello world!
step 3

Real problem

When my Ada library gets more complicated and requires following extra system DLLs:

  • libgnarl-7.dll

  • libgnat-7.dll

  • libgcc_s_seh-1.dll (mingw posix)

  • libwinpthreadthread-1.dll (mingw posix)

I am not able to exactly single out the code, which requires those extra DLLs, but once the code gets complicated enough to require these DLLs, it just stalls in the rust at the my_ffiinit function, outputting only:

step 1

Same code on x64 Linux just works. Also cross-compilation for other Linux platforms (powerpc, arm64) works.

When I use Library_Auto_Init="true", wine64 outputs:

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

Main project 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;

gpr for Precompiled shared library A:

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;

gpr for library B compiled from the source:

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;
like image 516
Kaarel Allemann Avatar asked Nov 20 '20 15:11

Kaarel Allemann


2 Answers

Problem is the use of for Library_Standalone use "encapsulated"; in the main project configuration file, when this is replaced with the default value standard project compilation fails with the following error:

shared library project "my_ffi" cannot import static library project "b"

Once line for Library_Kind use "relocatable"; is added to library B project configuration, compilation error goes away and libmy_ffi.dll is compiled successfully. What is more, resulting DLL works as expected when its called from Rust.

Quirks that remain unanswered:

  • Why is this not a problem for Linux platforms
  • Why does gprbuild not enforce or warn about the only static policy when the project is compiled
like image 140
Kaarel Allemann Avatar answered Oct 10 '22 10:10

Kaarel Allemann


Don't know much about Ada, but doesn't it have its own ABI? This document suggests that you need to tag a function with Convention => C in order to call it from C, similarly to how you'd use extern "C" in Rust.

like image 26
user1411900 Avatar answered Oct 10 '22 10:10

user1411900