#windows #dll #rust #ada #ffi
#Windows #dll #Ржавчина #ada #ффи
Вопрос:
Счастливый случай
Используя Mingw, я успешно скомпилировал минимальную библиотеку DLL Windows hello world в Ada и использовал ее через интерфейс 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;
#[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");
}
}
Что приводит к:
step 1
step 2
Hello world!
step 3
Реальная проблема
Когда моя библиотека Ada усложняется и требует следующих дополнительных системных библиотек DLL:
libgnarl-7.dll
libgnat-7.dll
libgcc_s_seh-1.dll
(mingw posix)libwinpthreadthread-1.dll
(mingw posix)
Я не могу точно выделить код, для которого требуются эти дополнительные библиотеки DLL, но как только код становится достаточно сложным, чтобы требовать эти библиотеки DLL, он просто зависает в rust в my_ffiinit
функции, выводя только:
step 1
Тот же код на 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
Георадар основного проекта:
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/" amp; external ("target", "linux64");
for Object_Dir use "obj/" amp; external ("target", "linux64");
end MY_FFI;
gpr для предварительно скомпилированной разделяемой библиотеки 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/" amp; external ("target", "linux64");
for Object_Dir use "obj/" amp; 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;
георадар для библиотеки B, скомпилированный из исходного кода:
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/" amp; external ("target", "linux64");
for Object_Dir use "obj/" amp; external ("target", "linux64");
end b_lib;
Комментарии:
1. Немного удивлен, потому
Ada.Text_IO
что реализован вlibgnat
. Вы пробовали сделать свою библиотеку автономной ? (требуется заклинание gprbuild)2. Я использую
for Library_Standalone use "encapsulated";
в своем файле проекта gprbuild. Однако моя более сложная версия библиотеки использует другие разделяемые библиотеки Ada в качестве зависимостей, которые не используютfor Library_Standalone use "encapsulated";
flag.3. Я никогда не использовал
encapsulated
, поэтому не уверен, но в руководстве говорится: «при использовании инкапсулированной библиотеки библиотека может зависеть только от статических библиотек»4. Обсуждение здесь, на comp.lang.ada, похоже, связано: groups.google.com/g/comp.lang.ada/c/Pz5bQBnO180/m/sgk49s-qAgAJ Лично я избегаю динамических библиотек и не могу комментировать потенциальные проблемы, с которыми можно столкнуться при их использовании.
5. Добавлен заголовок пакета и конфигурации для основного проекта и библиотек зависимостей. Я также успешно решил проблему благодаря комментарию @SimonWright об использовании
encapsulated
. Я отформатирую решение в форме ответа, как только смогу.
Ответ №1:
Проблема заключается в использовании for Library_Standalone use "encapsulated";
в главном файле конфигурации проекта, когда это значение заменяется значением по умолчанию standard
, компиляция проекта завершается ошибкой со следующей ошибкой:
shared library project "my_ffi" cannot import static library project "b"
Как только строка for Library_Kind use "relocatable";
добавляется в конфигурацию проекта library B, ошибка компиляции исчезает и libmy_ffi.dll
компилируется успешно. Более того, результирующая DLL работает должным образом при вызове из Rust.
Причуды, которые остаются без ответа:
- Почему это не проблема для платформ Linux
- Почему
gprbuild
не применяется или не предупреждает о единственной статической политике при компиляции проекта
Комментарии:
1. Я вспоминаю этот материал adainit (почему вы называете это FFI? Rust не является скриптом) был обходным путем против тупиковой ситуации Windows из-за запуска задач из DllInit. Это также может быть связано с вашей ситуацией
2. Вызываю его
my_fiiinit
, потому что именно так gprbuild переименовывает его. Когда имя библиотеки равно XXX, gprbuild переименовываетсяadainit
вXXXinit
.
Ответ №2:
Мало что знаю об Ada, но разве у нее нет собственного ABI? В этом документе предполагается, что вам нужно пометить функцию с Convention => C
помощью, чтобы вызвать ее из C, аналогично тому, как вы бы использовали extern "C"
в Rust.
Ответ №3:
Может быть, вы создаете задачи на уровне библиотеки в DLL: s? Это не сработает, поскольку init-код в динамических библиотеках строго однопоточный в Windows.