为什么在 JVM 下 运行 时我的 Ada 共享库上得到 "storage error"
Why am I getting "storage error" on my Ada shared library when running under a JVM
我们有一个由 GnatPro 19.2 编译的 Ada 共享库,我们通过 JNA 调用来调用它。
我们的应用程序在 windows 下运行良好。在 Linux 下移植它时,应用程序随机崩溃并出现 Ada 异常:
storage error or erroneous memory access.
使用 gdb 调试(附加进程)没有多大帮助。我们收到各种 SIGSEGV,我们继续,过了一会儿我们收到存储错误,没有可用的调用堆栈。
我们的共享库可以与 python 本机调用一起使用,没有任何问题。问题可能出在 Java 方面。
尝试切换 JVM(openjdk 或官方 jdk),但没有成功。
这是为什么?有解决办法吗?
第一个提示是在尝试将调试器附加到应用程序时收到一堆 SIGSEGV,然后在继续时看到程序恢复。
表示SIGSEGV信号在Java端处理,在Why does java app crash in gdb but runs normally in real life?中确认。
Java uses speculative loads. If a pointer points to addressable memory, the load succeeds. Rarely the pointer does not point to addressable memory, and the attempted load generates SIGSEGV ... which java runtime intercepts, makes the memory addressable again, and restarts the load instruction.
现在会发生什么,默认情况下,GNAT 运行-time 安装一个 new 信号处理程序来捕获 SIGSEGV 并重定向到一个干净的 Ada 异常。 Ada 异常的一个有趣特性是即使没有调试器,它们也可以打印堆栈跟踪。此 SIGSEGV 处理程序重定向允许这样做。
但在 Java 的情况下,由于 Java 使用投机负载,因此有时会在 java 端预期 SIGSEGV。所以当 Ada 共享库被加载和初始化时,Ada SIGSEGV 处理程序被安装,并捕获那些“正常”的 SIGSEGV,并立即中止。
请注意,在 Windows 下不会发生这种情况。 java 运行time 可能无法使用这种推测加载机制,因为在处理内存违规访问时存在 Windows 限制。
信号处理在s-intman.adb
中完成
-- Check that treatment of exception propagation here is consistent with
-- treatment of the abort signal in System.Task_Primitives.Operations.
case signo is
when SIGFPE => raise Constraint_Error;
when SIGILL => raise Program_Error;
-- when SIGSEGV => raise Storage_Error; -- commenting this line should fix it
when SIGBUS => raise Storage_Error;
when others => null;
end case;
end Notify_Exception;
现在我们必须重建一个新的原生 运行-time 并使用它来代替默认的。这非常乏味且容易出错。该文件是 gnarl 库的一部分。我们必须使用适当的选项动态重建 gnarl 库 -gnatp -nostdinc -O2 -fPIC
以创建 gnatrl 库替换...并在升级编译器时再次执行此操作...
幸运的是,AdaCore 提供了替代解决方案:
首先在 .gpr
项目目录中创建一个 pragmas 文件(我们称它为 no_sigsegv.adc
)包含:
pragma Interrupt_State (SIGSEGV, SYSTEM);
指示 运行-时间不要安装 SIGSEGV 处理程序
然后将其添加到 .gpr
文件的 Compiler
包中:
package Compiler is
...
for local_configuration_pragmas use Project'Project_dir & "/no_sigsegv.adc";
并从头开始重建一切。测试:没有任何崩溃。
我们有一个由 GnatPro 19.2 编译的 Ada 共享库,我们通过 JNA 调用来调用它。
我们的应用程序在 windows 下运行良好。在 Linux 下移植它时,应用程序随机崩溃并出现 Ada 异常:
storage error or erroneous memory access.
使用 gdb 调试(附加进程)没有多大帮助。我们收到各种 SIGSEGV,我们继续,过了一会儿我们收到存储错误,没有可用的调用堆栈。
我们的共享库可以与 python 本机调用一起使用,没有任何问题。问题可能出在 Java 方面。
尝试切换 JVM(openjdk 或官方 jdk),但没有成功。
这是为什么?有解决办法吗?
第一个提示是在尝试将调试器附加到应用程序时收到一堆 SIGSEGV,然后在继续时看到程序恢复。
表示SIGSEGV信号在Java端处理,在Why does java app crash in gdb but runs normally in real life?中确认。
Java uses speculative loads. If a pointer points to addressable memory, the load succeeds. Rarely the pointer does not point to addressable memory, and the attempted load generates SIGSEGV ... which java runtime intercepts, makes the memory addressable again, and restarts the load instruction.
现在会发生什么,默认情况下,GNAT 运行-time 安装一个 new 信号处理程序来捕获 SIGSEGV 并重定向到一个干净的 Ada 异常。 Ada 异常的一个有趣特性是即使没有调试器,它们也可以打印堆栈跟踪。此 SIGSEGV 处理程序重定向允许这样做。
但在 Java 的情况下,由于 Java 使用投机负载,因此有时会在 java 端预期 SIGSEGV。所以当 Ada 共享库被加载和初始化时,Ada SIGSEGV 处理程序被安装,并捕获那些“正常”的 SIGSEGV,并立即中止。
请注意,在 Windows 下不会发生这种情况。 java 运行time 可能无法使用这种推测加载机制,因为在处理内存违规访问时存在 Windows 限制。
信号处理在s-intman.adb
-- Check that treatment of exception propagation here is consistent with
-- treatment of the abort signal in System.Task_Primitives.Operations.
case signo is
when SIGFPE => raise Constraint_Error;
when SIGILL => raise Program_Error;
-- when SIGSEGV => raise Storage_Error; -- commenting this line should fix it
when SIGBUS => raise Storage_Error;
when others => null;
end case;
end Notify_Exception;
现在我们必须重建一个新的原生 运行-time 并使用它来代替默认的。这非常乏味且容易出错。该文件是 gnarl 库的一部分。我们必须使用适当的选项动态重建 gnarl 库 -gnatp -nostdinc -O2 -fPIC
以创建 gnatrl 库替换...并在升级编译器时再次执行此操作...
幸运的是,AdaCore 提供了替代解决方案:
首先在 .gpr
项目目录中创建一个 pragmas 文件(我们称它为 no_sigsegv.adc
)包含:
pragma Interrupt_State (SIGSEGV, SYSTEM);
指示 运行-时间不要安装 SIGSEGV 处理程序
然后将其添加到 .gpr
文件的 Compiler
包中:
package Compiler is
...
for local_configuration_pragmas use Project'Project_dir & "/no_sigsegv.adc";
并从头开始重建一切。测试:没有任何崩溃。