La estabilidad de la interfaz binaria de la aplicación (ABI) es un requisito previo actualizaciones exclusivas del framework, ya que los módulos de proveedores pueden depender del proveedor de código nativo Bibliotecas compartidas del kit de desarrollo (VNDK) que residen en la partición del sistema. En una versión de Android, las bibliotecas compartidas del VNDK recién compiladas deben Es compatible con ABI en bibliotecas compartidas para el VNDK, que se lanzaron anteriormente, por lo que los módulos de proveedores puede trabajar con esas bibliotecas sin recompilación y sin errores de tiempo de ejecución. Entre las versiones de Android, se pueden cambiar las bibliotecas de VNDK y no hay ABI. garantías.
Para ayudar a garantizar la compatibilidad con ABI, Android 9 incluye un verificador de ABI de encabezado, como se describe en las siguientes secciones.
Información acerca del cumplimiento del VNDK y la ABI
El VNDK es un conjunto restrictivo de bibliotecas a las que se pueden vincular los módulos de proveedores. que permiten actualizaciones solo del framework. El cumplimiento de las ABI se refiere al capacidad de una versión más reciente de una biblioteca compartida para funcionar como se espera con una que esté vinculado de forma dinámica a él (es decir, que funcione como una versión anterior del biblioteca).
Acerca de los símbolos exportados
Un símbolo exportado (también conocido como símbolo global) hace referencia a lo siguiente: un símbolo que cumpla con los siguientes requisitos:
- Se exportan mediante los encabezados públicos de una biblioteca compartida.
- Aparece en la tabla
.dynsym
del archivo.so
correspondiente a la biblioteca compartida. - Tiene vinculación DÉBIL o GLOBAL.
- La visibilidad es DEFAULT o PROTECTED.
- El índice de la sección no está SIN DEFINIR.
- El tipo es FUNC u OBJECT.
Los encabezados públicos de una biblioteca compartida se definen como los encabezados
que están disponibles para otras bibliotecas o objetos binarios
export_include_dirs
, export_header_lib_headers
export_static_lib_headers
,
export_shared_lib_headers
y
Atributos export_generated_headers
en Android.bp
del módulo correspondiente a la biblioteca compartida.
Acerca de los tipos accesibles
Un tipo accesible es cualquier tipo C/C++ integrado o definido por el usuario que se
se puede acceder de forma directa o indirecta mediante un símbolo exportado Y exportado
a través de encabezados públicos. Por ejemplo, libfoo.so
tiene una función
Foo
, que es un símbolo exportado que se encuentra en el
Tabla .dynsym
. La biblioteca libfoo.so
incluye lo siguiente:
lo siguiente:
foo_exported.h | foo.private.h. |
---|---|
typedef struct foo_private foo_private_t; typedef struct foo { int m1; int *m2; foo_private_t *mPfoo; } foo_t; typedef struct bar { foo_t mfoo; } bar_t; bool Foo(int id, bar_t *bar_ptr); |
typedef struct foo_private { int m1; float mbar; } foo_private_t; |
Android.bp |
---|
cc_library { name : libfoo, vendor_available: true, vndk { enabled : true, } srcs : ["src/*.cpp"], export_include_dirs : [ "exported" ], } |
Tabla .dynsym | |||||||
---|---|---|---|---|---|---|---|
Num
|
Value
|
Size
|
Type
|
Bind
|
Vis
|
Ndx
|
Name
|
1
|
0
|
0
|
FUNC
|
GLOB
|
DEF
|
UND
|
dlerror@libc
|
2
|
1ce0
|
20
|
FUNC
|
GLOB
|
DEF
|
12
|
Foo
|
Si observas Foo
, los tipos a los que se puede acceder de forma directa o indirecta incluyen los siguientes:
Tipo | Descripción |
---|---|
bool
|
Tipo de datos que se muestra de Foo .
|
int
|
Es el tipo del primer parámetro Foo .
|
bar_t *
|
Tipo del segundo parámetro Foo. Por bar_t * ,
bar_t se exporta a través de foo_exported.h .
bar_t contiene un miembro mfoo , del tipo
foo_t , que se exporta a través de foo_exported.h ,
lo que da como resultado la exportación de más tipos:
Sin embargo, NO se puede acceder a foo_private_t porque no es
se exportan a través de foo_exported.h . (foo_private_t * )
es opaca, por lo que se permiten cambios en foo_private_t ).
|
Se puede brindar una explicación similar para los tipos a los que se puede acceder mediante la clase básica. especificadores y parámetros de plantilla.
Cómo garantizar el cumplimiento de ABI
Se debe garantizar el cumplimiento con la ABI para las bibliotecas marcadas
vendor_available: true
y vndk.enabled: true
en la
los archivos Android.bp
correspondientes. Por ejemplo:
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
Para los tipos de datos a los que puede acceder una función exportada directa o indirectamente, Los siguientes cambios en una biblioteca se clasifican como roturas de ABI:
Tipo de datos | Descripción |
---|---|
Estructuras y clases |
|
Uniones |
|
Enumeraciones |
|
Símbolos globales |
|
* Las funciones de miembro públicas y privadas deben no se pueden cambiar ni quitar porque las funciones públicas intercaladas pueden hacer referencia funciones de miembro privado. Las referencias de símbolos para las funciones de miembros privados se mantienen en los objetos binarios del llamador. Cambia o quita las funciones privadas de los miembros de las bibliotecas compartidas puede generar objetos binarios incompatibles con versiones anteriores.
** Las compensaciones para los miembros de datos públicos o privados no deben porque las funciones intercaladas pueden referirse a estos miembros de datos en sus cuerpo de la función. Cambiar los desplazamientos de los miembros de datos puede generar objetos binarios incompatibles con versiones anteriores.
*** Aunque estos valores no cambian el diseño de la memoria del tipo, existen diferencias semánticas que podrían llevar a que las bibliotecas no funcionen como se espera.
Usa herramientas de cumplimiento de ABI
Cuando se compila una biblioteca de VNDK, se compara su ABI con el referencia de ABI correspondiente a la versión del VNDK que se está compilando. Referencias Los volcados de ABI se encuentran en:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
Por ejemplo, cuando compilas libfoo
para x86 en el nivel de API 27,
La ABI inferida de libfoo
se compara con su referencia en:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump
Error de falla de la ABI
Cuando se producen fallas de ABI, el registro de compilación muestra advertencias con el tipo de advertencia y una
la ruta de acceso
al informe abi-diff. Por ejemplo, si la ABI de libbinder
tiene
un cambio incompatible, el sistema de compilación arroja un mensaje de error
similar al siguiente:
***************************************************** error: VNDK library: libbinder.so's ABI has INCOMPATIBLE CHANGES Please check compatibility report at: out/soong/.intermediates/frameworks/native/libs/binder/libbinder/android_arm64_armv8-a_cortex-a73_vendor_shared/libbinder.so.abidiff ****************************************************** ---- Please update abi references by running platform/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder ----
Cómo compilar verificaciones de ABI de la biblioteca del VNDK
Cuando se compila una biblioteca de VNDK:
header-abi-dumper
procesa los archivos de origen compilados en compilar la biblioteca VNDK (los archivos de origen propios de la biblioteca, además de los archivos de origen) a través de dependencias transitivas estáticas), para producir Archivos.sdump
que corresponden a cada fuente.
Figura 1: Cómo crear .sdump
archivos- Luego,
header-abi-linker
procesa la.sdump
. archivos (mediante una secuencia de comandos de versión que se le proporcionó o el comando.so
correspondiente a la biblioteca compartida) para producir un.lsdump
que registra toda la información de ABI correspondiente a la biblioteca compartida.
Figura 2: Cómo crear .lsdump
archivo header-abi-diff
compara el.lsdump
Archivo con un archivo.lsdump
de referencia para producir un informe de diferencias que describe las diferencias en las ABI de ambas bibliotecas.
Figura 3: Crea el informe de diferencias
volador de encabezado abi
La herramienta header-abi-dumper
analiza un archivo de origen C/C++ y lo vuelca.
la ABI que se infiere de ese archivo de origen a un archivo intermedio. La compilación
el sistema ejecuta header-abi-dumper
en todos los archivos de origen compilados mientras
y también compilar una biblioteca que incluya los archivos de origen de la
dependencias.
Entradas |
|
---|---|
Salida | Es un archivo que describe la ABI del archivo de origen (por ejemplo,
foo.sdump representa la ABI de foo.cpp ).
|
Actualmente, los archivos .sdump
están en formato JSON, y no es así
y garantizar la estabilidad
en versiones futuras. Por lo tanto, .sdump
el formato de archivo debe considerarse un detalle de la implementación del sistema de compilación.
Por ejemplo, libfoo.so
tiene el siguiente archivo fuente
foo.cpp
:
#include <stdio.h> #include <foo_exported.h> bool Foo(int id, bar_t *bar_ptr) { if (id > 0 && bar_ptr->mfoo.m1 > 0) { return true; } return false; }
Puedes usar header-abi-dumper
para generar un nivel intermedio
archivo .sdump
que representa la ABI que presenta el archivo fuente
usando:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
Este comando le indica a header-abi-dumper
que analice
foo.cpp
con las marcas del compilador después de --
emiten la información de ABI exportada por los encabezados públicos en el
exported
. Lo siguiente es
foo.sdump
generado por
header-abi-dumper
:
{ "array_types" : [], "builtin_types" : [ { "alignment" : 4, "is_integral" : true, "linker_set_key" : "_ZTIi", "name" : "int", "referenced_type" : "_ZTIi", "self_type" : "_ZTIi", "size" : 4 } ], "elf_functions" : [], "elf_objects" : [], "enum_types" : [], "function_types" : [], "functions" : [ { "function_name" : "FooBad", "linker_set_key" : "_Z6FooBadiP3foo", "parameters" : [ { "referenced_type" : "_ZTIi" }, { "referenced_type" : "_ZTIP3foo" } ], "return_type" : "_ZTI3bar", "source_file" : "exported/foo_exported.h" } ], "global_vars" : [], "lvalue_reference_types" : [], "pointer_types" : [ { "alignment" : 8, "linker_set_key" : "_ZTIP11foo_private", "name" : "foo_private *", "referenced_type" : "_ZTI11foo_private", "self_type" : "_ZTIP11foo_private", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIP3foo", "name" : "foo *", "referenced_type" : "_ZTI3foo", "self_type" : "_ZTIP3foo", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIPi", "name" : "int *", "referenced_type" : "_ZTIi", "self_type" : "_ZTIPi", "size" : 8, "source_file" : "exported/foo_exported.h" } ], "qualified_types" : [], "record_types" : [ { "alignment" : 8, "fields" : [ { "field_name" : "mfoo", "referenced_type" : "_ZTI3foo" } ], "linker_set_key" : "_ZTI3bar", "name" : "bar", "referenced_type" : "_ZTI3bar", "self_type" : "_ZTI3bar", "size" : 24, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "fields" : [ { "field_name" : "m1", "referenced_type" : "_ZTIi" }, { "field_name" : "m2", "field_offset" : 64, "referenced_type" : "_ZTIPi" }, { "field_name" : "mPfoo", "field_offset" : 128, "referenced_type" : "_ZTIP11foo_private" } ], "linker_set_key" : "_ZTI3foo", "name" : "foo", "referenced_type" : "_ZTI3foo", "self_type" : "_ZTI3foo", "size" : 24, "source_file" : "exported/foo_exported.h" } ], "rvalue_reference_types" : [] }
foo.sdump
contiene información de ABI exportada por el archivo de origen.
foo.cpp
y los encabezados públicos, por ejemplo,
record_types
Hacer referencia a structs, uniones o clases definidas en los encabezados públicos. Cada tipo de registro tiene información sobre sus campos, sus tamaño, especificador de acceso, el archivo de encabezado en el que está definido y otros atributos.pointer_types
Cómo hacer referencia a los tipos de puntero de forma directa o indirecta a los que se hace referencia en los registros o funciones exportados en los encabezados públicos, junto con con el tipo al que apunta el puntero (mediante lareferenced_type
entype_info
). Se registra información similar en el Archivo.sdump
para tipos calificados, tipos C/C++ integrados, array tipos de referencia, y tipos de referencia lvalue y rvalue. Esa información permite diffing recursivo.functions
Representa funciones exportadas por encabezados públicos. También tienen información sobre el nombre alterado de la función, el tipo de datos que se muestra los tipos de parámetros, el especificador de acceso y otros atributos.
Vinculador de encabezados abi
La herramienta header-abi-linker
toma los archivos intermedios producidos
de header-abi-dumper
como entrada y, luego, vincula esos archivos:
Entradas |
|
---|---|
Salida | Es un archivo que describe la ABI de una biblioteca compartida (por ejemplo,
libfoo.so.lsdump representa la ABI de libfoo ).
|
La herramienta combina los gráficos de tipos en todos los archivos intermedios que se le proporcionaron.
considerar una definición (tipos definidos por el usuario en diferentes
unidades de traducción con el mismo nombre completamente calificado, podrían
diferentes) entre las unidades de traducción. Luego, la herramienta analiza
una secuencia de comandos de la versión o la tabla .dynsym
de la biblioteca compartida
(archivo .so
) para crear una lista de los símbolos exportados.
Por ejemplo, libfoo
consta de foo.cpp
y
bar.cpp
header-abi-linker
podría invocarse para
Crea el volcado completo de ABI vinculado de libfoo
de la siguiente manera:
header-abi-linker -I exported foo.sdump bar.sdump \ -o libfoo.so.lsdump \ -so libfoo.so \ -arch arm64 -api current
Ejemplo de resultado del comando en libfoo.so.lsdump
:
{ "array_types" : [], "builtin_types" : [ { "alignment" : 1, "is_integral" : true, "is_unsigned" : true, "linker_set_key" : "_ZTIb", "name" : "bool", "referenced_type" : "_ZTIb", "self_type" : "_ZTIb", "size" : 1 }, { "alignment" : 4, "is_integral" : true, "linker_set_key" : "_ZTIi", "name" : "int", "referenced_type" : "_ZTIi", "self_type" : "_ZTIi", "size" : 4 } ], "elf_functions" : [ { "name" : "_Z3FooiP3bar" }, { "name" : "_Z6FooBadiP3foo" } ], "elf_objects" : [], "enum_types" : [], "function_types" : [], "functions" : [ { "function_name" : "Foo", "linker_set_key" : "_Z3FooiP3bar", "parameters" : [ { "referenced_type" : "_ZTIi" }, { "referenced_type" : "_ZTIP3bar" } ], "return_type" : "_ZTIb", "source_file" : "exported/foo_exported.h" }, { "function_name" : "FooBad", "linker_set_key" : "_Z6FooBadiP3foo", "parameters" : [ { "referenced_type" : "_ZTIi" }, { "referenced_type" : "_ZTIP3foo" } ], "return_type" : "_ZTI3bar", "source_file" : "exported/foo_exported.h" } ], "global_vars" : [], "lvalue_reference_types" : [], "pointer_types" : [ { "alignment" : 8, "linker_set_key" : "_ZTIP11foo_private", "name" : "foo_private *", "referenced_type" : "_ZTI11foo_private", "self_type" : "_ZTIP11foo_private", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIP3bar", "name" : "bar *", "referenced_type" : "_ZTI3bar", "self_type" : "_ZTIP3bar", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIP3foo", "name" : "foo *", "referenced_type" : "_ZTI3foo", "self_type" : "_ZTIP3foo", "size" : 8, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "linker_set_key" : "_ZTIPi", "name" : "int *", "referenced_type" : "_ZTIi", "self_type" : "_ZTIPi", "size" : 8, "source_file" : "exported/foo_exported.h" } ], "qualified_types" : [], "record_types" : [ { "alignment" : 8, "fields" : [ { "field_name" : "mfoo", "referenced_type" : "_ZTI3foo" } ], "linker_set_key" : "_ZTI3bar", "name" : "bar", "referenced_type" : "_ZTI3bar", "self_type" : "_ZTI3bar", "size" : 24, "source_file" : "exported/foo_exported.h" }, { "alignment" : 8, "fields" : [ { "field_name" : "m1", "referenced_type" : "_ZTIi" }, { "field_name" : "m2", "field_offset" : 64, "referenced_type" : "_ZTIPi" }, { "field_name" : "mPfoo", "field_offset" : 128, "referenced_type" : "_ZTIP11foo_private" } ], "linker_set_key" : "_ZTI3foo", "name" : "foo", "referenced_type" : "_ZTI3foo", "self_type" : "_ZTI3foo", "size" : 24, "source_file" : "exported/foo_exported.h" } ], "rvalue_reference_types" : [] }
La herramienta header-abi-linker
realiza lo siguiente:
- Vincula los archivos
.sdump
proporcionados (foo.sdump
ybar.sdump
), filtrando la información de ABI que no está presente en los encabezados que se encuentran en el directorio:exported
. - Analiza
libfoo.so
y recopila información sobre los símbolos. que la biblioteca exporta a través de su tabla.dynsym
. - Se agregaron
_Z3FooiP3bar
y_Z6FooBadiP3foo
libfoo.so.lsdump
es el volcado final de ABI generado de
libfoo.so
encabezado-abi-diferencia
La herramienta header-abi-diff
compara dos archivos .lsdump
que representa la ABI de dos bibliotecas y produce un informe de diferencias que indica la
entre las dos ABI.
Entradas |
|
---|---|
Salida | Un informe de diferencias que indica las diferencias en las ABI que ofrecen ambos bibliotecas compartidas. |
El archivo de diferencias de ABI se encuentra Formato de texto protobuf. El formato está sujeto a cambios en versiones futuras.
Por ejemplo, tienes dos versiones de
libfoo
: libfoo_old.so
y
libfoo_new.so
En libfoo_new.so
, en
bar_t
, cambias el tipo de mfoo
de
foo_t
a foo_t *
. Como bar_t
es un
accesible, este debe marcarse como un cambio rotundo de ABI
header-abi-diff
Para ejecutar header-abi-diff
, haz lo siguiente:
header-abi-diff -old libfoo_old.so.lsdump \ -new libfoo_new.so.lsdump \ -arch arm64 \ -o libfoo.so.abidiff \ -lib libfoo
Ejemplo de resultado del comando en libfoo.so.abidiff
:
lib_name: "libfoo" arch: "arm64" record_type_diffs { name: "bar" type_stack: "Foo-> bar *->bar " type_info_diff { old_type_info { size: 24 alignment: 8 } new_type_info { size: 8 alignment: 8 } } fields_diff { old_field { referenced_type: "foo" field_offset: 0 field_name: "mfoo" access: public_access } new_field { referenced_type: "foo *" field_offset: 0 field_name: "mfoo" access: public_access } } }
libfoo.so.abidiff
contiene un informe de todas las fallas de ABI.
cambios en libfoo
. El mensaje record_type_diffs
indica que un registro cambió y enumera los cambios incompatibles,
incluyen:
- El tamaño del registro que cambia de
24
bytes a8
bytes. - El tipo de campo
mfoo
cambia defoo
afoo *
(se quitan todos los typedefs).
El campo type_stack
indica cómo header-abi-diff
alcanzó el tipo que cambió (bar
). Este campo puede ser
se interpreta como Foo
es una función exportada que incorpora
bar *
como parámetro, que apunta a bar
, que era
exportarse y cambiarse.
Cómo aplicar ABI y API
Para aplicar la ABI y la API de las bibliotecas compartidas del VNDK, las referencias de ABI deben
se anunciará en ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/
.
Para crear estas referencias, ejecuta el siguiente comando:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py
Después de crear las referencias, cualquier cambio realizado en el código fuente que resulte en un cambio incompatible de ABI/API en una biblioteca VNDK ahora generará un error de compilación.
Para actualizar las referencias de ABI de bibliotecas específicas, ejecuta el siguiente comando:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>
Por ejemplo, para actualizar las referencias de ABI de libbinder
, ejecuta lo siguiente:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder