Le premier indice est d'obtenir un tas de SIGSEGV lors de la tentative d'attachement d'un débogueur à l'application, puis de voir le programme reprendre lors de la poursuite.
Cela signifie que le signal SIGSEGV est géré du côté Java, comme confirmé dans Pourquoi l'application java plante-t-elle dans gdb mais fonctionne normalement en réalité?.
Java utilise des chargements spéculatifs. Si un pointeur pointe vers une mémoire adressable, le chargement réussit. Rarement, le pointeur ne pointe pas vers une mémoire adressable, et le chargement tenté génère un SIGSEGV ... que le runtime Java intercepte, rend la mémoire à nouveau adressable et redémarre l'instruction de chargement.
Maintenant, ce qui se passe, c'est que par défaut, le runtime GNAT installe un nouveau gestionnaire de signal pour capturer les SIGSEGV et rediriger vers une exception Ada propre. Une caractéristique intéressante des exceptions Ada est qu'elles peuvent afficher la trace de la pile, même sans débogueur. Cette redirection du gestionnaire SIGSEGV le permet.
Mais dans le cas de Java, étant donné que Java utilise des chargements spéculatifs, les SIGSEGV sont attendus de temps en temps du côté Java. Donc, lorsque la bibliothèque partagée Ada a été chargée et initialisée, le gestionnaire SIGSEGV Ada est installé, capture ces SIGSEGV "normaux" et s'arrête immédiatement.
Notez que cela ne se produit pas sous Windows. Le runtime Java ne peut probablement pas utiliser ce mécanisme de chargement spéculatif en raison des limitations de Windows lors de la gestion des accès par violation de mémoire.
La gestion des signaux se fait dans s-intman.adb
-- Vérifier que le traitement de la propagation d'exception ici est cohérent avec
-- le traitement du signal d'abandon dans les opérations System.Task_Primitives.
case signo is
when SIGFPE => raise Constraint_Error;
when SIGILL => raise Program_Error;
-- when SIGSEGV => raise Storage_Error; -- en commentant cette ligne, cela devrait corriger le problème
when SIGBUS => raise Storage_Error;
when others => null;
end case;
end Notify_Exception;
Maintenant, il faudrait reconstruire un nouveau runtime natif et l'utiliser à la place de celui par défaut. C'est assez fastidieux et source d'erreurs. Ce fichier fait partie de la bibliothèque gnarl. Nous devrions reconstruire la bibliothèque gnarl dynamiquement avec les bonnes options -gnatp -nostdinc -O2 -fPIC
pour créer une substitution de bibliothèque gnatrl ... et refaire cela lors de la mise à niveau du compilateur...
Heureusement, AdaCore a fourni une solution alternative:
Créez d'abord un fichier de pragmas dans le répertoire du projet .gpr
(appelons-le no_sigsegv.adc
) contenant:
pragma Interrupt_State (SIGSEGV, SYSTEM);
pour indiquer au runtime de ne pas installer le gestionnaire SIGSEGV
Ensuite, ajoutez ceci au package Compiler
du fichier .gpr
:
package Compiler is
...
for local_configuration_pragmas use Project'Project_dir & "/no_sigsegv.adc";
et reconstruisez tout depuis le début. Test : pas un seul crash en aucun cas.