Problèmes courants et solutions

Ce document représente une liste partielle des non-bugs les plus courants que vous pouvez rencontrer lors de l'utilisation du NDK. Il fournit également des solutions (le cas échéant).

Utiliser _FILE_OFFSET_BITS=64 avec des anciens niveaux d'API

Avant les en-têtes unifiés, le NDK n'acceptait pas _FILE_OFFSET_BITS=64. Si vous avez défini cette fonction lors de la création de votre application, elle a été ignorée sans notification. L'option _FILE_OFFSET_BITS=64 est désormais compatible avec les en-têtes unifiés, mais sur les anciennes versions d'Android, très peu d'API off_t étaient disponibles en tant que variante off64_t. Par conséquent, l'utilisation de cette fonctionnalité avec les anciens niveaux d'API réduit le nombre de fonctions disponibles.

Ce problème est expliqué en détail dans l'article de blog r16 et dans la documentation spécifique à l'implémentation Bionic.

Problème : votre build demande des API qui n'existent pas dans votre minSdkVersion.

Solution : désactivez _FILE_OFFSET_BITS=64 ou augmentez votre minSdkVersion.

Définition non déclarée ou implicite de mmap

L'erreur suivante peut s'afficher en C++ :

error: use of undeclared identifier 'mmap' (erreur : utilisation de l'identifiant non déclaré "mmap")

ou l'erreur suivante en C :

warning: implicit declaration of function 'mmap' is invalid in C99 (avertissement : La déclaration implicite de la fonction "mmap" n'est pas valide dans C99)

L'utilisation de _FILE_OFFSET_BITS=64 indique à la bibliothèque C de choisir mmap64 au lieu de mmap. mmap64 n'était pas disponible avant android-21. Si la valeur de minSdkVersion est inférieure à 21, la bibliothèque C ne contient pas de mmap compatible avec _FILE_OFFSET_BITS=64. La fonction n'est donc pas disponible.

Niveau de minSdkVersion supérieur au niveau d'API de l'appareil

Le niveau d'API que vous utilisez pour la compilation avec le NDK a une signification très différente de celle de compileSdkVersion pour Java. Le niveau d'API du NDK correspond au niveau d'API minimal qui est compatible avec votre application. Dans ndk-build, il s'agit du paramètre APP_PLATFORM. Avec CMake, il s'agit de -DANDROID_PLATFORM.

Étant donné que les références aux fonctions sont généralement résolues lors du chargement des bibliothèques plutôt que lors de leur premier appel, vous ne pouvez pas référencer des API qui ne sont pas toujours présentes et protéger leur utilisation avec des vérifications au niveau de l'API. Si elles sont référencées, elles doivent être présentes.

Problème : le niveau d'API du NDK est supérieur à celui de l'API compatible avec votre appareil.

Solution : définissez le niveau d'API du NDK (APP_PLATFORM) sur la version minimale d'Android compatible avec votre application.

Système de compilation Paramètre
ndk-build APP_PLATFORM
CMake ANDROID_PLATFORM
externalNativeBuild android.minSdkVersion

Pour les autres systèmes de compilation, consultez Utiliser le NDK avec d'autres systèmes de compilation.

Impossible de localiser les symboles __aeabi

Le message suivant :

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__aeabi_memcpy" (UnlessLinkError : échec de dlopen : impossible de localiser le symbole "__aeabi_memcpy")

est un exemple d'erreur d'exécution possible. Ces erreurs apparaissent dans le journal lorsque vous tentez de charger vos bibliothèques natives. Le symbole peut être toute variante d'__aeabi_*. __aeabi_memcpy et __aeabi_memclr semblent être les plus courants.

Ce cas de figure est documenté dans le problème 126.

Impossible de localiser le symbole rand

Pour le message de journal d'erreur suivant :

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "rand" (UnlessLinkError : échec de dlopen : impossible de localiser le symbole "rand")

Consultez cette réponse de Stack Overflow.

Référence non définie à __atomic_*

Problème : certaines ABI ont besoin de libatomic afin de fournir des implémentations pour les opérations atomiques.

Solution : ajoutez -latomic lors de l'association.

Pour le message d'erreur suivant :

error: undefined reference to '__atomic_exchange_4' (erreur : référence non définie à "__atomic_exchange_4")

Le symbole réel peut être n'importe quel préfixe commençant par __atomic_.

RTTI et les exceptions ne fonctionnent pas au-delà des limites de la bibliothèque

Problème : les exceptions ne sont pas détectées lorsqu'elles sont générées au-delà des limites de la bibliothèque partagée ou lorsque dynamic_cast échoue.

Solution : ajoutez une fonction clé à vos types. Une fonction clé est la première fonction virtuelle non pure et non connectée pour un type. Pour consulter un exemple, reportez-vous à la discussion sur le problème 533.

L'ABI C++ indique que deux objets ont le même type si et seulement si leurs pointeurs type_info sont identiques. Les exceptions ne peuvent être capturées que si type_info correspond à l'exception générée. La même règle s'applique à dynamic_cast.

Lorsqu'un type ne comporte pas de fonction clé, son typeinfo est émis en tant que symbole faible, et les informations sur le type de correspondance sont fusionnées lors du chargement des bibliothèques. Lorsque vous chargez des bibliothèques de manière dynamique après le chargement du fichier exécutable (en d'autres termes, via dlopen ou System.loadLibrary), il est possible que le chargeur ne parvienne pas à fusionner les informations sur le type pour les bibliothèques chargées. Dans ce cas, les deux types ne sont pas considérés comme égaux.

Utiliser des bibliothèques prédéfinies non concordantes

L'utilisation de bibliothèques prédéfinies (généralement des bibliothèques tierces) dans votre application demande un peu plus d'attention. En règle générale, tenez compte des règles suivantes :

  • Le niveau d'API minimal de l'application généré est le maximum de minSdkVersion pour toutes les bibliothèques de l'application.

    Si votre minSdkVersion correspond à 16, mais que vous utilisez une bibliothèque prédéfinie créée avec le niveau 21, le niveau d'API minimal de l'application généré sera 21. Le non-respect de cette condition sera visible au moment de la compilation si la bibliothèque prédéfinie est statique. Toutefois, il est possible qu'il n'apparaisse qu'au moment de l'exécution des bibliothèques partagées prédéfinies.

  • Toutes les bibliothèques doivent être générées avec la même version de NDK.

    Cette règle est un peu plus flexible que la plupart des cas de figure, car les défaillances sont rares. Cependant, la compatibilité entre les bibliothèques créées avec différentes versions majeures du NDK n'est pas garantie. L'ABI C++ n'est pas stable et a changé par le passé.

  • Les applications avec plusieurs bibliothèques partagées doivent utiliser une bibliothèque STL partagée.

    Comme pour les STL non concordantes, les problèmes causés par cette approche peuvent être évités en prenant le plus grand soin, mais il est préférable de ne prendre aucun risque. Pour ce faire, évitez d'avoir plusieurs bibliothèques partagées dans votre application.