Problemas comunes y soluciones

Este documento es una lista parcial de los problemas que no se consideran errores y que sueles encontrar cuando usas el NDK, así como una lista de sus soluciones (si están disponibles).

Cómo usar _FILE_OFFSET_BITS=64 con niveles de API anteriores

Antes de los encabezados unificados, el NDK no admitía _FILE_OFFSET_BITS=64. Si definiste este elemento durante el proceso de compilación de tu app, se lo ignoró con sigilo. Ahora, la opción _FILE_OFFSET_BITS=64 es compatible con encabezados unificados, pero en versiones anteriores de Android, muy pocas API de off_t estaban disponibles como una variante off64_t. Por lo tanto, el uso de esta función con niveles de API anteriores genera, como consecuencia, menos funciones disponibles.

Este problema se explica en detalle en la entrada de blog de r16 y en la documentación de bionic.

Problema: Tu compilación solicita API que no existen en el elemento minSdkVersion.

Solución: Inhabilita _FILE_OFFSET_BITS=64 o aumenta tu elemento minSdkVersion.

Definición implícita o sin especificar de mmap

Es posible que veas el siguiente error en C++:

error: use of undeclared identifier 'mmap'

o el siguiente error en C:

warning: implicit declaration of function 'mmap' is invalid in C99

Usar _FILE_OFFSET_BITS=64 le indica a la biblioteca C que utilice mmap64 en lugar de mmap. mmap64 no estaba disponible hasta android-21. Si el valor de minSdkVersion es inferior a 21, la biblioteca C no contiene un elemento mmap que sea compatible con _FILE_OFFSET_BITS=64, por lo que la función no está disponible.

Se estableció el elemento minSdkVersion más alto que el nivel de API del dispositivo

El nivel de API que compilas en función del NDK tiene un significado muy diferente que el objeto compileSdkVersion para Java. El nivel de API del NDK es el nivel de API mínimo que admite tu app. En ndk-build, esta es tu configuración de APP_PLATFORM. Con CMake, es -DANDROID_PLATFORM.

Como las referencias a las funciones suelen resolverse cuando las bibliotecas se cargan en lugar de cuando se las llama por primera vez, no puedes hacer referencia a las API que no siempre están presentes ni proteger su uso con verificaciones del nivel de API. En caso de que se haga referencia a estas, deben estar presentes.

Problema: El nivel de API del NDK es más alto que el de la API compatible con tu dispositivo.

Solución: Configura el nivel de API del NDK (APP_PLATFORM) en la versión mínima de Android que admite tu app.

Sistema de compilación Configuración
ndk-build APP_PLATFORM
CMake ANDROID_PLATFORM
externalNativeBuild android.minSdkVersion

Para otros sistemas de compilación, consulta Cómo usar el NDK con otros sistemas de compilación.

No se pueden ubicar los símbolos __aeabi.

El siguiente mensaje:

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__aeabi_memcpy"

es un ejemplo de posibles errores de runtime. Estos errores aparecen en el registro cuando intentas cargar tus bibliotecas nativas. El símbolo puede ser cualquiera de __aeabi_*; parece que __aeabi_memcpy y __aeabi_memclr son los más comunes.

Este problema está documentado en el Problema 126.

No se puede ubicar el símbolo rand.

Para el siguiente mensaje de registro de errores:

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "rand"

Consulta esta respuesta de Stack Overflow detallada.

Referencia indefinida a __atomic_*

Problema: Algunas ABI necesitan libatomic a fin de proporcionar algunas implementaciones para operaciones atómicas.

Solución: Agrega -latomic durante la vinculación.

Para el siguiente mensaje de error:

error: undefined reference to '__atomic_exchange_4'

el símbolo real aquí podría tener cualquier prefijo con __atomic_.

La RTTI o las excepciones no funcionan entre los límites de la biblioteca

Problema: Las excepciones no se detectan cuando se superan los límites de la biblioteca compartida o cuando dynamic_cast falla.

Solución: Agrega una función de clave a tus tipos. Una función clave es la primera función virtual no pura, fuera de línea, para un tipo. Para ver un ejemplo, consulta el debate sobre el Problema 533.

La ABI de C++ indica que dos objetos tienen el mismo tipo siempre y cuando sus punteros type_info sean idénticos. Solo se pueden detectar excepciones si el elemento type_info de la captura coincide con la excepción arrojada. Se aplica la misma regla para dynamic_cast.

Cuando un tipo no tiene una función clave, su typeinfo se emite como un símbolo débil, y la información del tipo de coincidencia se combina cuando se cargan las bibliotecas. Cuando se cargan bibliotecas de forma dinámica después de que el ejecutable se haya cargado (en otras palabras, a través de dlopen o System.loadLibrary), es posible que el cargador no pueda combinar la información del tipo para las bibliotecas cargadas. Cuando esto sucede, los dos tipos no se consideran idénticos.

Cómo usar bibliotecas compiladas previamente que no coinciden

Usar en tu aplicación bibliotecas compiladas previamente, que suelen ser de terceros, requiere un poco de cuidado adicional. En general, ten en cuenta las siguientes reglas:

  • El nivel de API mínimo de la app resultante es el máximo de los elementos minSdkVersion de todas las bibliotecas de la app.

    Si minSdkVersion es 16, pero usas una biblioteca compilada previamente en 21, el nivel mínimo de API resultante de la app es 21. Si no se cumple, se verá en el tiempo de compilación si la biblioteca compilada previamente es estática, pero es posible que no aparezca hasta el tiempo de ejecución para las bibliotecas compartidas y compiladas previamente.

  • Todas las bibliotecas deben generarse con la misma versión del NDK.

    Esta regla es un poco más flexible que la mayoría, ya que las fallas son poco frecuentes, pero no se garantiza la compatibilidad entre las bibliotecas que se compilaron con diferentes versiones principales del NDK. La ABI de C++ no es estable y cambió en el pasado.

  • Las apps con varias bibliotecas compartidas deben usar una STL compartida.

    Al igual que con las STL que no coinciden, los problemas que causa esto se pueden evitar si se tiene mucho cuidado, pero es mejor eludir el problema. La mejor manera de hacerlo es evitar tener varias bibliotecas compartidas en tu app.