Está en la página 1de 7

ABI de Android

Los diferentes dispositivos Android usan distintas CPU que, a su vez, admiten varios conjuntos de
instrucciones. Cada combinación de CPU y conjunto de instrucciones tiene su propia interfaz binaria de
aplicación (ABI). Una ABI incluye la siguiente información:

El conjunto de instrucciones de CPU (y las extensiones) que se pueden usar

El formato endian de las cargas y los almacenamientos en memoria durante el tiempo de ejecución
(Android siempre es little endian)

Convenciones para pasar datos entre aplicaciones y el sistema, que incluyen restricciones de alineación y
la manera en que el sistema usa la pila y los registros al llamar a funciones

El formato de objetos binarios ejecutables, como programas y bibliotecas compartidas, y los tipos de
contenido que admiten (Android siempre usa ELF; para obtener más información, consulta Interfaz
binaria de aplicación System V de ELF)

Cómo se alteran nombres de C++ (para obtener más información, consulta ABI Itanium C++ y genérica)

En esta página, se enumeran las ABI que admite el NDK y se proporciona información sobre cómo funciona
cada una de ellas.

Las ABI también pueden hacer referencia a la API nativa compatible con la plataforma. Para obtener una lista
de esos tipos de problemas de ABI que afectan a los sistemas de 32 bits, consulta errores de ABI de 32 bits .

ABI admitidas
Tabla 1: ABI y conjuntos de instrucciones admitidos

ABI Conjuntos de instrucciones admitidos Notas

armeabi-v7a armeabi No es compatible con dispositivos ARMv5 y v6.

Thumb-2

VFPv3-D16

arm64-v8a AArch64

x86 x86 (IA-32) No admite MOVBE o SSE4.

MMX

SSE/2/3

SSSE3

x86_64 x86-64

MMX

SSE/2/3

SSSE3

SSE4.1, 4.2
POPCNT

Nota: Históricamente, el NDK admitía ARMv5 (armeabi) y MIPS de 32 bits y 64 bits, pero la compatibilidad con esas
ABI se quitó en el NDK r17.

armeabi-v7a

Esta ABI es para CPU basadas en ARM de 32 bits. La variante de Android incluye Thumb-2 y las instrucciones
del punto flotante de hardware VFP, específicamente VFPv3-D16, que incluye 16 registros de punto flotante de
64 bits dedicados.

Para obtener información sobre las partes de la ABI que no son específicas de Android, consulta Interfaz
binaria de la aplicación (ABI) para la arquitectura ARM.

Los sistemas de compilación del NDK generan código Thumb-2 de manera predeterminada a menos que
uses LOCAL_ARM_MODE en tu Android.mk para ndk-build o ANDROID_ARM_MODE cuando configuras CMake.

Otras extensiones que incluyen Advanced SIMD (Neon) y VFPv3-D32 son opcionales. Para obtener más
información, consulta Compatibilidad con Neon.

La ABI armeabi-v7a usa -mfloat-abi=softfp para forzar la regla de que, aunque el sistema pueda ejecutar


código de punto flotante, el compilador debe pasar todos los valores float en registros para enteros y todos
los valores double en pares de registros para enteros cuando realiza llamadas a funciones.

arm64-v8a

Esta ABI es para CPU basadas en ARMv8-A, que admiten la arquitectura AArch64 de 64 bits. Incluye las
extensiones de arquitectura Advanced SIMD (Neon).

Puedes usar los objetos intrínsecos de Neon en el código C y C++ para aprovechar la extensión Advanced
SIMD. La Guía del programador de Neon para Armv8-A proporciona más información sobre los objetos
intrínsecos de Neon y la programación de Neon en general.

Consulta Aprende la arquitectura de Arm para obtener detalles completos de las partes de la ABI que no son
específicas de Android. Arm también ofrece algunos consejos de portabilidad en Desarrollo de Android de
64 bits.

En Android, el registro x18 específico de la plataforma está reservado para ShadowCallStack y tu código no


debe tocarlo. Las versiones actuales de Clang usan, de forma predeterminada, la opción -ffixed-x18 en
Android, así que, a menos que tengas un ensamblador manual (o un compilador muy antiguo), no deberías
preocuparte.

x86

Esta ABI es para CPU que admiten el conjunto de instrucciones comúnmente conocido como "x86", "i386" o
"IA-32". Entre las características de esta ABI se incluyen las siguientes:

Instrucciones normalmente generadas por GCC con marcas de compilador, como las siguientes:
-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32

Estas marcas se orientan al conjunto de instrucciones Pentium Pro, junto con las extensiones de
conjuntos de instrucciones MMX, SSE, SSE2, SSE3 y SSSE3. El código generado es una optimización
equilibrada entre las principales CPU Intel de 32 bits.

Para obtener más información sobre las marcas de compilador (en particular en lo que tiene que ver con
la optimización del rendimiento), consulta las sugerencias de rendimiento de x86 de GCC.

Uso de la convención de llamada estándar de 32 bits de x86 de Linux, en lugar de la de SVR. Para obtener
más información, consulta la sección 6, "Uso de registros", del documento Convenciones de llamada para
diferentes compiladores C++ y sistemas operativos.

La ABI no incluye ninguna otra extensión opcional del conjunto de instrucciones IA-32, como las siguientes:

MOVBE

Cualquier variante de SSE4

De todas maneras, podrás usar estas extensiones, siempre y cuando utilices sondeo de funciones en tiempo
de ejecución para habilitarlas y proporciones reservas para los dispositivos que no las admitan.

El conjunto de herramientas del NDK supone una alineación de pila de 16 bytes antes de una llamada a una
función. Las herramientas y las opciones predeterminadas aplican esta regla. Si escribes código de
ensamblado, debes asegurarte de mantener la alineación de la pila y de que los demás compiladores también
cumplan esta regla.

Para obtener más información, consulta los siguientes documentos:

Convenciones de llamada para diferentes compiladores de C++ y sistemas operativos

Manual para desarrolladores de software para la arquitectura Intel IA-32, Volumen 2: referencia de


conjunto de instrucciones

Manual para desarrolladores de software para la arquitectura Intel IA-32, Volumen 3: guía de


programación del sistema

Interfaz Binaria de Aplicación System V: Complemento de arquitectura para el procesador Intel 386

x86_64

Esta ABI es para CPU que admiten el conjunto de instrucciones conocido como "x86-64". Admite instrucciones
que GCC suele generar con las siguientes marcas de compilador:

-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel

Estas marcas se orientan al conjunto de instrucciones x86-64, de acuerdo con la documentación de GCC, junto
con las extensiones de conjuntos de instrucciones MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 y POPCNT. El
código generado es una optimización equilibrada entre las principales CPU Intel de 64 bits.
Para obtener más información sobre las marcas de compilador (en particular en lo que tiene que ver con la
optimización del rendimiento), consulta las sugerencias de rendimiento de x86 de GCC.

Esta ABI no incluye ninguna otra extensión opcional del conjunto de instrucciones x86-64, como las siguientes:

MOVBE

SHA

AVX

AVX2

De todas maneras, podrás usar estas extensiones, siempre y cuando utilices sondeo de funciones en tiempo
de ejecución para habilitarlas y proporciones reservas para los dispositivos que no las admitan.

Para obtener más información, consulta los siguientes documentos:

Convenciones de llamada para diferentes compiladores de C++ y sistemas operativos

Manual para desarrolladores de software para arquitecturas Intel 64 e IA-32, Volumen 2: referencia de
conjunto de instrucciones

Manual para desarrolladores para arquitecturas Intel 64 e IA-32, volumen 3: programación del sistema

Cómo generar de código para ABI específicas

GRADLENDK-BUILDCMAKE

Gradle (ya sea que se use a través de Android Studio o desde la línea de comandos) se compila para todas las ABI no
obsoletas de forma predeterminada. Para restringir el conjunto de ABI que admite tu aplicación, usa abiFilters. Por
ejemplo, para compilar solo ABI de 64 bits, establece la siguiente configuración en tu build.gradle:

android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}

De manera predeterminada, el sistema de compilación incluye los objetos binarios para cada ABI en un solo
APK, también conocido como APK multiarquitectura. Un APK multiarquitectura es considerablemente más
grande que el que solo contiene objetos binarios para una sola ABI; de este modo, se obtiene mayor
compatibilidad, pero a costa de un APK más grande. Te recomendamos que aproveches los paquetes de
aplicaciones o divisiones de APK para reducir el tamaño de tus archivos y mantener la máxima compatibilidad
del dispositivo.

Durante la instalación, el administrador de paquetes solo debe desempaquetar el código máquina más
apropiado para el dispositivo de destino. Para obtener más detalles, consulta Extracción automática de código
nativo al momento de la instalación.

Administración de ABI en la plataforma de Android


En esta sección, se proporciona información sobre cómo la plataforma de Android administra el código nativo
en los APK.

Código nativo en paquetes de apps

Tanto Play Store como el administrador de paquetes esperan encontrar bibliotecas generadas con NDK en las
rutas de acceso a archivos dentro del APK que coincidan con el siguiente patrón:

/lib/<abi>/lib<name>.so

En este caso, <abi> es uno de los nombres de ABI que aparecen en ABI admitidas y <name> es el nombre de la
biblioteca tal como la definiste para la variable LOCAL_MODULE en el archivo Android.mk. Como los archivos APK
son solo archivos ZIP, es muy fácil abrirlos para confirmar que las bibliotecas nativas compartidas están en el
lugar correspondiente.

Si el sistema no encuentra las bibliotecas nativas compartidas donde espera que estén, entonces no podrá
usarlas. En ese caso, es la misma app la que tiene que copiar las bibliotecas y, luego, ejecutar dlopen().

En el APK multiarquitectura, cada biblioteca se aloja en un directorio cuyo nombre coincide con una ABI
correspondiente. Por ejemplo, un APK multiarquitectura puede contener lo siguiente:

/lib/armeabi/libfoo.so
/lib/armeabi-v7a/libfoo.so
/lib/arm64-v8a/libfoo.so
/lib/x86/libfoo.so
/lib/x86_64/libfoo.so

Nota: Los dispositivos Android basados en ARMv7 que ejecutan versiones 4.0.3 o anteriores instalan bibliotecas
nativas desde el directorio armeabi en lugar de hacerlo desde armeabi-v7a, si existen ambos directorios. Esto se debe a
que /lib/armeabi/ viene después de /lib/armeabi-v7a/ en el APK. Este problema se corrigió a partir de la
versión 4.0.4.

Compatibilidad de ABI en la plataforma de Android

En el momento de la ejecución, el sistema Android sabe cuáles ABI admite, porque las propiedades del sistema
específicas de la compilación indican lo siguiente:

La ABI principal del dispositivo, que corresponde al código máquina que se usa en la imagen del sistema.

De manera opcional, ABI secundarias, que corresponden a otra ABI que la imagen del sistema también
admite.

Este mecanismo garantiza que el sistema extraiga el mejor código máquina del paquete durante la instalación.

Para obtener un mejor rendimiento, debes compilar directamente para la ABI principal. Por ejemplo, un
dispositivo típico basado en ARMv5TE solo definiría la ABI principal: armeabi. Por el contrario, un dispositivo
típico basado en ARMv7 definiría la ABI principal como armeabi-v7a y la secundaria como armeabi, ya que
puede ejecutar objetos binarios nativos de la aplicación generados para cada una de ellas.

Los dispositivos de 64 bits también admiten variantes de 32 bits. Si tomamos dispositivos arm64-v8a como
ejemplo, el dispositivo también puede ejecutar código armeabi y armeabi-v7a. Sin embargo, ten en cuenta que
tu app tendrá un mejor rendimiento en dispositivos de 64 bits si se orienta a arm64-v8a que si depende de un
dispositivo que ejecuta la versión armeabi-v7a de tu app.

Muchos dispositivos basados en x86 también pueden ejecutar objetos binarios armeabi-v7a y armeabi del NDK.
Para esos dispositivos, la ABI principal sería x86, y la secundaria, armeabi-v7a.

Puedes forzar la instalación de un APK para una ABI específica. Esto es útil para pruebas. Usa el siguiente
comando:

adb install --abi abi-identifier path_to_apk

Extracción automática de código nativo al momento de la instalación

Cuando instales una app, el servicio de administrador de paquetes escanea el APK y busca bibliotecas
compartidas con el siguiente formato:

lib/<primary-abi>/lib<name>.so

Si no encuentra ninguna, y definiste una ABI secundaria, el servicio busca bibliotecas compartidas con el
siguiente formato:

lib/<secondary-abi>/lib<name>.so

Cuando encuentra las bibliotecas que está buscando, el administrador de paquetes las copia
a /lib/lib<name>.so, en el directorio de bibliotecas nativas de la aplicación (<nativeLibraryDir>/). Los
siguientes fragmentos recuperan el nativeLibraryDir:

KOTLINJAVA

import android.content.pm.PackageInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
...
val ainfo = this.applicationContext.packageManager.getApplicationInfo(
"com.domain.app",
PackageManager.GET_SHARED_LIBRARY_FILES
)
Log.v(TAG, "native library dir ${ainfo.nativeLibraryDir}")

Si no se encuentra ningún archivo de objetos compartidos, la aplicación se compila y se instala, pero falla en el
tiempo de ejecución.

También podría gustarte