O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Estabilidade ABI

A estabilidade da interface binária do aplicativo (ABI) é um pré-requisito das atualizações apenas da estrutura porque os módulos do fornecedor podem depender das bibliotecas compartilhadas do Vendor Native Development Kit (VNDK) que residem na partição do sistema. Em uma versão do Android, as bibliotecas compartilhadas VNDK recém-construídas devem ser compatíveis com ABI com as bibliotecas compartilhadas VNDK lançadas anteriormente para que os módulos do fornecedor possam trabalhar com essas bibliotecas sem recompilação e sem erros de tempo de execução. Entre as versões do Android, as bibliotecas VNDK podem ser alteradas e não há garantias de ABI.

Para ajudar a garantir a compatibilidade ABI, o Android 9 inclui um verificador de cabeçalho ABI, conforme descrito nas seções a seguir.

Sobre conformidade com VNDK e ABI

O VNDK é um conjunto restritivo de bibliotecas às quais os módulos do fornecedor podem se vincular e que permitem atualizações apenas da estrutura. Cumprimento ABI refere-se à capacidade de uma versão mais recente de uma biblioteca compartilhada para o trabalho conforme o esperado com um módulo que é dinamicamente ligado a ele (ou seja, funciona como uma versão mais antiga da biblioteca faria).

Sobre símbolos exportados

Um símbolo exportado (também conhecido como um símbolo global) refere-se a um símbolo que satisfaça todas as seguintes:

  • Exportado pelos cabeçalhos públicas de uma biblioteca compartilhada.
  • Aparece no .dynsym mesa do .so arquivo correspondente à biblioteca compartilhada.
  • Possui ligação FRACA ou GLOBAL.
  • A visibilidade é PADRÃO ou PROTEGIDA.
  • O índice da seção não é UNDEFINED.
  • O tipo é FUNC ou OBJECT.

Os cabeçalhos públicas de uma biblioteca compartilhada são definidos como os cabeçalhos disponíveis para outras bibliotecas / binários através dos export_include_dirs , export_header_lib_headers , export_static_lib_headers , export_shared_lib_headers e export_generated_headers atributos em Android.bp definições do módulo correspondente à biblioteca compartilhada.

Sobre os tipos alcançáveis

Um tipo é alcançável qualquer C / C ++ embutido ou tipo definido pelo utilizador que é acessível directamente ou indirectamente através de um símbolo exportados e exportado através cabeçalhos públicas. Por exemplo, libfoo.so tem a função Foo , que é um símbolo exportado encontrada no .dynsym mesa. O libfoo.so biblioteca inclui o seguinte:

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"
  ],
}
.dynsym table
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

Olhando para Foo , tipos alcançáveis diretos / indiretos incluem:

Modelo Descrição
bool Tipo de retorno de Foo .
int Tipo de primeira Foo parâmetro.
bar_t * Tipo do segundo parâmetro Foo. A título de bar_t * , bar_t é exportado através foo_exported.h .

bar_t contém um membro mfoo , de tipo foo_t , que é exportado através foo_exported.h , o que resulta em mais tipos de ser exportado:
  • int : é o tipo de m1 .
  • int * : é o tipo de m2 .
  • foo_private_t * : é o tipo de mPfoo .

No entanto, foo_private_t não está acessível porque não é exportado através foo_exported.h . ( foot_private_t * é opaca, por conseguinte, as alterações feitas para foo_private_t são permitidos.)

Uma explicação semelhante pode ser fornecida para tipos acessíveis por meio de especificadores de classe base e parâmetros de modelo também.

Garantindo a conformidade com a ABI

Cumprimento ABI deve ser assegurada para as bibliotecas marcados vendor_available: true e vndk.enabled: true nos correspondentes Android.bp arquivos. Por exemplo:

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

Para tipos de dados alcançáveis ​​direta ou indiretamente por uma função exportada, as seguintes alterações em uma biblioteca são classificadas como quebra de ABI:

Tipo de dados Descrição
Estruturas e classes
  • Altere o tamanho do tipo de classe ou do tipo de estrutura.
  • Classes base
    • Adicione ou remova classes básicas.
    • Adicione ou remova classes base virtualmente herdadas.
    • Altere a ordem das classes básicas.
  • Funções de membro
    • Remover funções de membro *.
    • Adicione ou remova argumentos de funções-membro.
    • Altere os tipos de argumento ou os tipos de retorno de funções de membro *.
    • Altere o layout da mesa virtual.
  • Membros de dados
    • Remova membros de dados estáticos.
    • Adicione ou remova membros de dados não estáticos.
    • Altere os tipos de membros de dados.
    • Altere os deslocamentos para membros de dados não estáticos **.
    • Alterar o const , volatile , e / ou restricted qualificadores de membros de dados ***.
    • Faça downgrade dos especificadores de acesso de membros de dados ***.
  • Altere os argumentos do modelo.
Sindicatos
  • Adicione ou remova membros de dados.
  • Altere o tamanho do tipo de união.
  • Altere os tipos de membros de dados.
  • Altere a ordem dos membros dos dados.
Enumerações
  • Altere o tipo subjacente.
  • Mude os nomes dos enumeradores.
  • Altere os valores dos enumeradores.
Símbolos Globais
  • Remova os símbolos exportados por cabeçalhos públicos.
  • Para símbolos globais do tipo FUNC
    • Adicione ou remova argumentos.
    • Altere os tipos de argumento.
    • Altere o tipo de retorno.
    • Faça downgrade do especificador de acesso ***.
  • Para símbolos globais do tipo OBJECT
    • Altere o tipo C / C ++ correspondente.
    • Faça downgrade do especificador de acesso ***.

* Ambas as funções membro públicas e privadas não deve ser alterada ou removida porque funções inline públicas pode se referir a funções de membro privadas. As referências de símbolo a funções de membro privadas podem ser mantidas em binários do chamador. Alterar ou remover funções-membro privadas de bibliotecas compartilhadas pode resultar em binários incompatíveis com versões anteriores.

** Os deslocamentos para membros de dados públicos ou privados não deve ser alterado porque funções inline pode se referir a esses membros de dados em seu corpo função. Alterar deslocamentos de membros de dados pode resultar em binários incompatíveis com versões anteriores.

*** Apesar de não alterar o layout de memória do tipo, há diferenças semânticas que poderiam levar a bibliotecas não funcionam como esperado.

Usando ferramentas de conformidade ABI

Quando uma biblioteca VNDK é construída, a ABI da biblioteca é comparada com a referência ABI correspondente para a versão do VNDK que está sendo construída. Os despejos de ABI de referência estão localizados em:

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

Por exemplo, na construção de libfoo para o nível API 27 do VNDK, libfoo é inferida ABI é comparado com a sua referência em:

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

Erro de quebra de ABI

Em quebras de ABI, o log de construção exibe avisos com o tipo de aviso e um caminho para o relatório abi-diff. Por exemplo, se libbinder 's ABI tem uma mudança incompatível, o sistema de compilação gera um erro com uma mensagem semelhante ao seguinte:

*****************************************************
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 ----

Construindo verificações de ABI da biblioteca VNDK

Quando uma biblioteca VNDK é construída:

  1. header-abi-dumper processa os arquivos de origem compilados para criar a biblioteca VNDK (próprios arquivos de origem da biblioteca, bem como arquivos de origem herdadas através de dependências transitivas estáticos), para produzir .sdump arquivos que correspondem a cada fonte.
    sdump creation
    Figura 1. Criando as .sdump arquivos
  2. header-abi-linker , em seguida, processa os .sdump arquivos (usando um script versão que lhe foi fornecida ou o .so arquivo correspondente à biblioteca compartilhada) para produzir um .lsdump arquivo que registra todas as informações ABI correspondente à biblioteca compartilhada.
    lsdump creation
    Figura 2. Criando o .lsdump arquivo
  3. header-abi-diff compara o .lsdump arquivo com uma referência .lsdump arquivo para produzir um relatório diff que descreve as diferenças nas ABIs das duas bibliotecas.
    abi diff creation
    Figura 3. Criar o relatório diff

header-abi-dumper

O header-abi-dumper ferramenta analisa um ficheiro de fonte de C / C ++ e despeja o ABI inferida a partir desse ficheiro fonte para um ficheiro intermediário. O sistema de compilação é executado header-abi-dumper em todos os arquivos de origem compilados ao mesmo tempo, a construção de uma biblioteca que inclui os arquivos de origem de dependências transitivas.

Atualmente .sdump arquivos são formatados como Protobuf TextFormatted , o que não é garantido para ser estável em futuros lançamentos. Como tal, .sdump formatação de arquivo deve ser considerada uma implementação detalhe sistema de compilação.

Por exemplo, libfoo.so tem o seguinte arquivo de origem 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;
}

Você pode usar o header-abi-dumper para gerar um intermediário .sdump arquivo que representa o ABI apresentado pelo arquivo de origem usando:

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

Este comando diz header-abi-dumper para analisar foo.cpp e emitem a informação ABI que é exposto nos cabeçalhos públicas na exported diretório. Este é um trecho (não é uma representação completa) de foo.sdump gerado pelo 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 contém informações ABI exposta pelo arquivo de origem foo.cpp , por exemplo:

  • record_types . Consulte estruturas, sindicatos ou classes expostas pelos cabeçalhos públicos. Cada tipo de registro possui informações sobre seus campos, seu tamanho, especificador de acesso, o arquivo de cabeçalho em que foi exposto, etc.
  • pointer_types . Referem-se a tipos de ponteiro directamente / indirectamente referenciados por registros / funções expostas por cabeçalhos públicas, juntamente com o tipo o apontador aponta (via o referenced_type campo em type_info ). Informação semelhante está registado na .sdump arquivo para tipos qualificados, embutidos C / C ++ tipos, tipos de matriz, e os tipos de referência lvalue e rvalue (tais informações de registo sobre os tipos permite diffing recursiva).
  • functions . Representam funções expostas por cabeçalhos públicos. Eles também têm informações sobre o nome mutilado da função, o tipo de retorno, os tipos de parâmetros, o especificador de acesso, etc.

header-abi-linker

O header-abi-linker ferramenta leva os arquivos intermediários produzidos pelo header-abi-dumper como entrada, em seguida, liga esses ficheiros:

Entradas
  • Arquivos intermediários produzidos pelo header-abi-dumper
  • Script de versão / arquivo de mapa (opcional)
  • .so arquivo da biblioteca compartilhada
Saída Um arquivo que registra a ABI de uma biblioteca compartilhada (por exemplo libfoo.so.lsdump representa libfoo ABI 's).

A ferramenta mescla os gráficos de tipos em todos os arquivos intermediários fornecidos a ela, levando em consideração diferenças de uma definição (tipos definidos pelo usuário em unidades de tradução diferentes com o mesmo nome totalmente qualificado, podem ser semanticamente diferentes) entre as unidades de tradução. A ferramenta analisa quer um script versão ou o .dynsym mesa da biblioteca compartilhada ( .so arquivo) para fazer uma lista dos símbolos exportados.

Por exemplo, quando libfoo adiciona o bar.cpp arquivo (que expõe uma função C bar ) à sua compilação, header-abi-linker poderia ser invocado para criar o despejo ABI ligada completa de libfoo da seguinte forma:

header-abi-linker -I exported foo.sdump bar.sdump \
                  -o libfoo.so.lsdump \
                  -so libfoo.so \
                  -arch arm64 -api current

Saída de comando exemplo em 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"
}

O header-abi-linker ferramenta:

  • Ligações os .sdump arquivos fornecidos a ele ( foo.sdump e bar.sdump ), filtrando as informações ABI não está presente nos cabeçalhos que residem no diretório: exported .
  • Interpreta libfoo.so , e coleta informações sobre os símbolos exportados pela biblioteca através da sua .dynsym mesa.
  • Adiciona _Z3FooiP3bar e Bar .

libfoo.so.lsdump é o despejo ABI gerado final libfoo.so .

header-abi-diff

O header-abi-diff ferramenta compara dois .lsdump arquivos que representam a ABI de duas bibliotecas e produz um relatório diff indicando as diferenças entre os dois ABIs.

Entradas
  • .lsdump arquivo representando a ABI de uma biblioteca compartilhado antigo.
  • .lsdump arquivo representando a ABI de uma nova biblioteca compartilhada.
Saída Um relatório diferente indicando as diferenças nas ABIs oferecidas pelas duas bibliotecas compartilhadas comparadas.

O arquivo diff ABI é projetado para ser o mais detalhado e legível possível. O formato está sujeito a alterações em versões futuras. Por exemplo, você tem duas versões do libfoo : libfoo_old.so e libfoo_new.so . Em libfoo_new.so , em bar_t , você alterar o tipo de mfoo de foo_t para foo_t * . Desde bar_t é um tipo directamente acessível, este deve ser marcada como uma alteração de quebra ABI pelo header-abi-diff .

Para executar header-abi-diff :

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

Saída de comando exemplo em 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
    }
  }
}

O libfoo.so.abidiff contém um relatório de todas as alterações ABI quebrando em libfoo . O record_type_diffs mensagem indica um registro foi alterado e lista as alterações incompatíveis, que incluem:

  • O tamanho da ficha mudando de 24 bytes de 8 bytes.
  • O tipo de campo de mfoo mudando de foo para foo * (todas as TYPEDEFs são removidos).

O type_stack campo indica como header-abi-diff atingiu o tipo que mudou ( bar ). Este campo pode ser interpretada como Foo é uma função exportada que leva em bar * como parâmetro, que aponta para bar , que foi exportado e mudou.

Aplicação de ABI / API

Para cumprir a ABI / API de VNDK e LLNDK bibliotecas compartilhadas, referências ABI devem ser verificados em ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/ . Para criar essas referências, execute o seguinte comando:

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

Depois de criar as referências, qualquer alteração feita no código-fonte que resulte em uma alteração ABI / API incompatível em uma biblioteca VNDK ou LLNDK agora resulta em um erro de construção.

Para atualizar as referências ABI para bibliotecas VNDK específicas, execute o seguinte comando:

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

Por exemplo, para atualizar libbinder referências ABI, execute:

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

Para atualizar as referências ABI para bibliotecas LLNDK específicas, execute o seguinte comando:

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

Por exemplo, para atualizar libm referências ABI, execute:

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