Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Estabilidad ABI

La estabilidad de la Interfaz binaria de la aplicación (ABI) es un requisito previo para las actualizaciones de solo marco porque los módulos del proveedor pueden depender de las bibliotecas compartidas del Kit de desarrollo nativo del proveedor (VNDK) que residen en la partición del sistema. Dentro de una versión de Android, las bibliotecas compartidas de VNDK recién creadas deben ser compatibles con ABI con las bibliotecas compartidas de VNDK publicadas anteriormente para que los módulos de los proveedores puedan trabajar con esas bibliotecas sin tener que volver a compilarlas y sin errores de tiempo de ejecución. Entre las versiones de Android, las bibliotecas VNDK se pueden cambiar y no hay garantías de ABI.

Para ayudar a garantizar la compatibilidad ABI, Android 9 incluye un verificador ABI de encabezado, como se describe en las siguientes secciones.

Acerca del cumplimiento de VNDK y ABI

El VNDK es un conjunto restrictivo de bibliotecas a las que los módulos de los proveedores pueden vincularse y que permiten actualizaciones de solo marco. ABI cumplimiento se refiere a la capacidad de una versión más reciente de una biblioteca compartida para el trabajo como se esperaba con un módulo que se vincula dinámicamente a la misma (es decir, funciona como una versión más antigua de la biblioteca lo haría).

Acerca de los símbolos exportados

Un símbolo exportado (también conocido como un símbolo global) se refiere a un símbolo que satisface todos los siguientes:

  • Exportado por las cabeceras públicas de una biblioteca compartida.
  • Aparece en la .dynsym mesa del .so fichero correspondiente a la biblioteca compartida.
  • Tiene unión DÉBIL o GLOBAL.
  • La visibilidad es POR DEFECTO o PROTEGIDA.
  • El índice de sección NO ESTÁ INDEFINIDO.
  • El tipo es FUNC u OBJECT.

Las cabeceras públicas de una biblioteca compartida se definen como las cabeceras disponibles a otras bibliotecas / binarios a través de los export_include_dirs , export_header_lib_headers , export_static_lib_headers , export_shared_lib_headers y export_generated_headers atributos en Android.bp definiciones del módulo correspondiente a la biblioteca compartida.

Acerca de los tipos accesibles

Un tipo alcanzable es cualquier C / C ++ incorporado o tipo definido por el usuario que es accesible directa o indirectamente a través de un símbolo exportado y se exportan a través de cabeceras públicas. Por ejemplo, libfoo.so tiene la función de Foo , que es un símbolo exportado encuentra en el .dynsym tabla. El libfoo.so biblioteca incluye 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 : [
    "include"
  ],
}
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

En cuanto a Foo , tipos alcanzables directas / indirectas incluyen:

Escribe Descripción
bool Tipo de retorno de Foo .
int Tipo de la primera Foo parámetro.
bar_t * Tipo de segundo parámetro de Foo. A modo de bar_t * , bar_t se exporta a través foo_exported.h .

bar_t contiene un miembro de mfoo , de tipo foo_t , que se exporta a través de foo_exported.h , lo que resulta en más tipos se exportan:
  • int : es el tipo de m1 .
  • int * : es el tipo de m2 .
  • foo_private_t * : es el tipo de mPfoo .

Sin embargo, foo_private_t no es accesible debido a que no se exporta a través foo_exported.h . ( foot_private_t * es opaco, por lo tanto los cambios realizados en foo_private_t se admiten.)

Se puede dar una explicación similar para los tipos accesibles a través de especificadores de clase base y parámetros de plantilla también.

Asegurar el cumplimiento de ABI

ABI cumplimiento debe garantizarse para las bibliotecas marcadas vendor_available: true y vndk.enabled: true en las correspondientes Android.bp archivos. Por ejemplo:

cc_library {
    name: "libvndk_example",
    vendor_available: true,
    vndk: {
        enabled: true,
    }
}

Para los tipos de datos accesibles directa o indirectamente por una función exportada, los siguientes cambios en una biblioteca se clasifican como ruptura de ABI:

Tipo de datos Descripción
Estructuras y clases
  • Cambie el tamaño del tipo de clase o el tipo de estructura.
  • Clases base
    • Agregue o elimine clases base.
    • Agregue o elimine clases base heredadas virtualmente.
    • Cambie el orden de las clases base.
  • Funciones de los miembros
    • Eliminar funciones miembro *.
    • Agregue o elimine argumentos de funciones miembro.
    • Cambie los tipos de argumentos o los tipos de retorno de funciones miembro *.
    • Cambia el diseño de la mesa virtual.
  • Miembros de datos
    • Eliminar miembros de datos estáticos.
    • Agregue o elimine miembros de datos no estáticos.
    • Cambie los tipos de miembros de datos.
    • Cambie las compensaciones a miembros de datos no estáticos **.
    • Cambiar la const , volatile , y / o restricted calificadores de los miembros de datos ***.
    • Reduzca los especificadores de acceso de los miembros de datos ***.
  • Cambie los argumentos de la plantilla.
Sindicatos
  • Agregue o elimine miembros de datos.
  • Cambie el tamaño del tipo de unión.
  • Cambie los tipos de miembros de datos.
  • Cambie el orden de los miembros de datos.
Enumeraciones
  • Cambie el tipo subyacente.
  • Cambie los nombres de los enumeradores.
  • Cambie los valores de los enumeradores.
Símbolos Globales
  • Elimina los símbolos exportados por encabezados públicos.
  • Para símbolos globales de tipo FUNC
    • Agregue o elimine argumentos.
    • Cambie los tipos de argumentos.
    • Cambie el tipo de devolución.
    • Baje la categoría del especificador de acceso ***.
  • Para símbolos globales de tipo OBJECT
    • Cambie el tipo de C / C ++ correspondiente.
    • Baje la categoría del especificador de acceso ***.

* Ambas funciones miembro públicas y privadas no deben modificarse o eliminarse debido a las funciones en línea públicas pueden referirse a las funciones miembro privadas. Las referencias de símbolos a funciones de miembros privados se pueden mantener en binarios de llamadas. Cambiar o eliminar funciones de miembros privados de bibliotecas compartidas puede resultar en binarios incompatibles con versiones anteriores.

** Las compensaciones a los miembros de datos públicos o privados no deben modificarse debido a las funciones en línea pueden hacer referencia a estos miembros de datos en su cuerpo de la función. Cambiar las compensaciones de los miembros de datos puede resultar en binarios incompatibles con versiones anteriores.

*** Si bien estos no cambian el diseño de memoria del tipo, hay diferencias semánticas que podrían conducir a las bibliotecas no funcionan como se esperaba.

Uso de herramientas de cumplimiento de ABI

Cuando se crea una biblioteca VNDK, la ABI de la biblioteca se compara con la referencia ABI correspondiente para la versión del VNDK que se está construyendo. Los volcados de ABI de referencia se encuentran en:

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/<${PLATFORM_VNDK_VERSION}>/<BINDER_BITNESS>/<ARCH_ARCH-VARIANT>/source-based

Por ejemplo, en la construcción de libfoo para el nivel API 27 de la VNDK, libfoo 'S inferirse ABI se compara con su referencia en:

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/27/64/<ARCH_ARCH-VARIANT>/source-based/libfoo.so.lsdump

Error de rotura ABI

En caso de roturas de ABI, el registro de compilación muestra advertencias con el tipo de advertencia y una ruta al informe abi-diff. Por ejemplo, si libbinder 's ABI tiene un cambio incompatible, el sistema de construcción arroja un error con un mensaje 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 ----

Creación de comprobaciones ABI de la biblioteca VNDK

Cuando se crea una biblioteca VNDK:

  1. header-abi-dumper procesa los archivos fuente compilados para construir la biblioteca VNDK (propios archivos de origen de la biblioteca, así como archivos de origen heredadas a través de dependencias transitivas estáticos), para producir .sdump archivos que corresponden a cada fuente.
    sdump creation
    Figura 1. Creación de los .sdump archivos
  2. header-abi-linker luego procesa los .sdump archivos (utilizando un script versión proporcionada a ella o la .so fichero correspondiente a la biblioteca compartida) para producir un .lsdump archivo que registra toda la información ABI correspondiente a la biblioteca compartida.
    lsdump creation
    Figura 2. Creación de la .lsdump archivo
  3. header-abi-diff compara el .lsdump archivo con una referencia .lsdump archivo para producir un informe diff que describe las diferencias en las ITB de las dos bibliotecas.
    abi diff creation
    Figura 3. Crear el informe diff

cabecera-abi-dumper

La header-abi-dumper herramienta analiza un archivo fuente de C / C ++ y vuelca el ABI inferirse de ese archivo de origen en un archivo intermedio. El sistema de construcción se ejecuta header-abi-dumper en todos los archivos de código compilado y al mismo tiempo la construcción de una biblioteca que incluye los archivos de origen de dependencias transitivas.

Actualmente .sdump archivos tienen el formato Protobuf TextFormatted , lo que no se garantiza que sea estable en futuras versiones. Como tal, .sdump formato de archivo debe ser considerada como una aplicación detallada sistema de construcción.

Por ejemplo, libfoo.so tiene el siguiente archivo de origen 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;
}

Puede utilizar header-abi-dumper para generar un intermedio .sdump archivo que representa el ABI presentado por el archivo de origen usando:

$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -x c++

Esta orden le dice header-abi-dumper para analizar foo.cpp y emiten la información ABI que se expone en las cabeceras públicas en el exported directorio. Este es un extracto (no es una representación completa) de foo.sdump generada por header-abi-dumper :

record_types {
  type_info {
    name: "foo"
    size: 12
    alignment: 4
    referenced_type: "type-1"
    source_file: "foo/include/foo_exported.h"
    linker_set_key: "foo"
    self_type: "type-1"
  }
  fields {
    referenced_type: "type-2"
    field_offset: 0
    field_name: "m1"
    access: public_access
  }
  fields {
    referenced_type: "type-3"
    field_offset: 32
    field_name: "m2"
    access: public_access
  }
  fields {
    referenced_type: "type-5"
    field_offset: 64
    field_name: "mPfoo"
    access: public_access
  }
  access: public_access
  record_kind: struct_kind
  tag_info {
    unique_id: "_ZTS3foo"
  }
}
record_types {
  type_info {
    name: "bar"
    size: 12
    alignment: 4
    referenced_type: "type-6"
…
pointer_types {
  type_info {
    name: "bar *"
    size: 4
    alignment: 4
    referenced_type: "type-6"
    source_file: "foo/include/foo_exported.h"
    linker_set_key: "bar *"
    self_type: "type-8"
  }
}
builtin_types {
  type_info {
    name: "int"
    size: 4
    alignment: 4
    referenced_type: "type-2"
    source_file: ""
    linker_set_key: "int"
    self_type: "type-2"
  }
  is_unsigned: false
  is_integral: true
}
functions {
  return_type: "type-7"
  function_name: "Foo"
  source_file: "foo/include/foo_exported.h"
  parameters {
    referenced_type: "type-2"
    default_arg: false
  }
  parameters {
    referenced_type: "type-8"
    default_arg: false
  }
  linker_set_key: "_Z3FooiP3bar"
  access: public_access
}

foo.sdump contiene información ABI expuesta por el archivo de origen foo.cpp , por ejemplo:

  • record_types . Consulte estructuras, uniones o clases expuestas por los encabezados públicos. Cada tipo de registro tiene información sobre sus campos, su tamaño, especificador de acceso, el archivo de encabezado en el que se expuso, etc.
  • pointer_types . Se refieren a los tipos de puntero directa / indirecta referenciados por los registros / funciones expuestas por las cabeceras públicas, junto con el tipo el puntero apunta al (vía referenced_type campo en type_info ). Información similar se registra en el .sdump archivo para tipos cualificados, una función de C / C ++ tipos, tipos de matriz, y los tipos de referencia y lvalue rvalue (como la información de registro sobre los tipos permite para diferenciar recursiva).
  • functions . Representa funciones expuestas por encabezados públicos. También tienen información sobre el nombre alterado de la función, el tipo de retorno, los tipos de parámetros, el especificador de acceso, etc.

encabezado-abi-enlazador

La header-abi-linker herramienta toma los archivos intermedios producidos por header-abi-dumper como entrada a continuación enlaza esos archivos:

Entradas
  • Archivos intermedios producidos por la header-abi-dumper
  • Script de versión / archivo de mapa (opcional)
  • .so archivo de la biblioteca compartida
Producción Un archivo que registra el ABI de una biblioteca compartida (por ejemplo libfoo.so.lsdump representa libfoo ABI 's).

La herramienta fusiona los gráficos de tipos en todos los archivos intermedios que se le asignan, teniendo en cuenta las diferencias de una definición (los tipos definidos por el usuario en diferentes unidades de traducción con el mismo nombre completo, pueden ser semánticamente diferentes) entre las unidades de traducción. La herramienta analiza a continuación, o bien una secuencia de comandos de versión o el .dynsym mesa de la biblioteca compartida ( .so archivo) para hacer una lista de los símbolos exportados.

Por ejemplo, cuando libfoo añade el bar.cpp archivo (que expone una función C bar ) para su compilación, header-abi-linker podría ser invocada para crear el volcado completo ABI enlazada 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 salida de comando en libfoo.so.lsdump :

record_types {
  type_info {
    name: "foo"
    size: 24
    alignment: 8
    referenced_type: "type-1"
    source_file: "foo/include/foo_exported.h"
    linker_set_key: "foo"
    self_type: "type-1"
  }
  fields {
    referenced_type: "type-2"
    field_offset: 0
    field_name: "m1"
    access: public_access
  }
  fields {
    referenced_type: "type-3"
    field_offset: 64
    field_name: "m2"
    access: public_access
  }
  fields {
    referenced_type: "type-4"
    field_offset: 128
    field_name: "mPfoo"
    access: public_access
  }
  access: public_access
  record_kind: struct_kind
  tag_info {
    unique_id: "_ZTS3foo"
  }
}
record_types {
  type_info {
    name: "bar"
    size: 24
    alignment: 8
...
builtin_types {
  type_info {
    name: "void"
    size: 0
    alignment: 0
    referenced_type: "type-6"
    source_file: ""
    linker_set_key: "void"
    self_type: "type-6"
  }
  is_unsigned: false
  is_integral: false
}
functions {
  return_type: "type-19"
  function_name: "Foo"
  source_file: "foo/include/foo_exported.h"
  parameters {
    referenced_type: "type-2"
    default_arg: false
  }
  parameters {
    referenced_type: "type-20"
    default_arg: false
  }
  linker_set_key: "_Z3FooiP3bar"
  access: public_access
}
functions {
  return_type: "type-6"
  function_name: "FooBad"
  source_file: "foo/include/foo_exported_bad.h"
  parameters {
    referenced_type: "type-2"
    default_arg: false
  }
parameters {
    referenced_type: "type-7"
    default_arg: false
  }
  linker_set_key: "_Z6FooBadiP3foo"
  access: public_access
}
elf_functions {
  name: "_Z3FooiP3bar"
}
elf_functions {
  name: "_Z6FooBadiP3foo"
}

La header-abi-linker herramienta:

  • Enlaces las .sdump archivos proporcionados a ella ( foo.sdump y bar.sdump ), filtrando la información ABI no está presente en las cabeceras que residen en el directorio: exported .
  • Analiza libfoo.so , y recoge información sobre los símbolos exportados por la biblioteca a través de su .dynsym mesa.
  • Añade _Z3FooiP3bar y Bar .

libfoo.so.lsdump es el último volcado ABI generada de libfoo.so .

encabezado-abi-diff

La header-abi-diff herramienta compara dos .lsdump archivos que representan la ITB de dos bibliotecas y produce un informe diff indicando las diferencias entre los dos ABIs.

Entradas
  • .lsdump fichero que representa la ITB de una vieja biblioteca compartida.
  • .lsdump fichero que representa la ITB de una nueva biblioteca compartida.
Producción Un informe de diferencias que indica las diferencias en las ABI que ofrecen las dos bibliotecas compartidas comparadas.

El archivo de diferencias ABI está diseñado para ser lo más detallado y legible posible. El formato está sujeto a cambios en futuras versiones. Por ejemplo, tiene dos versiones de libfoo : libfoo_old.so y libfoo_new.so . En libfoo_new.so , en bar_t , se cambia el tipo de mfoo de foo_t a foo_t * . Desde bar_t es un tipo directamente accesible, esto debe ser marcado como un cambio de última hora ABI por header-abi-diff .

Para ejecutar header-abi-diff :

header-abi-diff -old libfoo_old.so.lsdump \
                -new libfoo_new.so.lsdump \
                -arch arm64 \
                -o libfoo.so.abidiff \
                -lib libfoo

Ejemplo salida de 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
    }
  }
}

El libfoo.so.abidiff contiene un informe de todos los cambios ABI rotura en libfoo . El record_type_diffs mensaje indica un registro ha cambiado y las listas de los cambios incompatibles, que incluyen:

  • El tamaño del registro se cambia de 24 bytes a 8 bytes.
  • El tipo de campo de mfoo cambiar de foo a foo * (todos typedefs se quitaron).

El type_stack campo indica cómo header-abi-diff alcanzó el tipo que cambió ( bar ). Este campo puede ser interpretado como Foo es una función exportada que lleva en bar * como parámetro, que apunta a bar , que se exporta y se cambió.

Aplicación de ABI / API

Para hacer efectiva la ABI / API de VNDK y LLNDK bibliotecas compartidas, las referencias ABI deben comprobarse en ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/ . Para crear estas referencias, ejecute 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 de ABI / API incompatible en una biblioteca VNDK o LLNDK ahora genera un error de compilación.

Para actualizar las referencias ABI para bibliotecas centrales de VNDK específicas, ejecute el siguiente comando:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>

Por ejemplo, para actualizar libbinder referencias ABI, ejecute:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder

Para actualizar las referencias ABI para bibliotecas LLNDK específicas, ejecute el siguiente comando:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2> --llndk

Por ejemplo, para actualizar libm referencias ABI, ejecute:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libm --llndk