Você pode usar as ferramentas de monitoramento de interface binária (ABI) do aplicativo, disponíveis no Android 11 e superior, para estabilizar a ABI no kernel dos kernels do Android. As ferramentas coletam e comparam representações ABI de binários de kernel existentes ( vmlinux
+ módulos). Essas representações ABI são os arquivos .xml
e as listas de símbolos. A interface na qual a representação fornece uma visão é chamada de Kernel Module Interfaces (KMIs). Você pode usar as ferramentas para rastrear e mitigar as alterações no KMI.
A ferramenta de monitoramento ABI é desenvolvida em AOSP e usa libabigail
para gerar e comparar representações.
Esta página descreve as ferramentas, o processo de coleta e análise de representações de ABI e o uso de tais representações para fornecer estabilidade à ABI no kernel. Esta página também fornece informações para contribuir com alterações nos kernels do Android.
Este diretório contém as ferramentas específicas para a análise ABI. Use-o com os scripts de compilação fornecidos por build_abi.sh
.)
Processo
A análise da ABI do kernel envolve várias etapas, a maioria das quais pode ser automatizada:
- Adquira a cadeia de ferramentas, construa scripts e fontes de kernel por meio de
repo
. - Forneça quaisquer pré-requisitos (como a biblioteca
libabigail
e a coleção de ferramentas). - Construa o kernel e sua representação ABI .
- Analise as diferenças de ABI entre a compilação e uma referência .
- Atualize a representação ABI (se necessário) .
- Trabalhe com listas de símbolos .
As instruções a seguir funcionam para qualquer kernel que você possa compilar usando uma cadeia de ferramentas compatível (como a cadeia de ferramentas Clang pré-compilada). Os repo manifests
de repositório estão disponíveis para todas as ramificações de kernel comuns do Android e para vários kernels específicos do dispositivo, eles garantem que a cadeia de ferramentas correta seja usada ao criar uma distribuição de kernel para análise.
Use as ferramentas de monitoramento ABI
1. Adquira a cadeia de ferramentas, crie scripts e fontes do kernel por meio do repositório
Você pode adquirir a cadeia de ferramentas, construir scripts (esses scripts) e fontes do kernel com repo
. Para obter documentação detalhada, consulte as informações correspondentes para compilar kernels do Android .
Para ilustrar o processo, as etapas a seguir usam common-android12-5.10
, uma ramificação do kernel do Android, que é o kernel GKI mais recente lançado no momento da redação deste artigo. Para obter essa ramificação por meio de repo
, execute o seguinte:
repo init -u https://android.googlesource.com/kernel/manifest -b common-android12-5.10
repo sync
2. Forneça pré-requisitos
O conjunto de ferramentas ABI usa libabigail
, uma biblioteca e uma coleção de ferramentas, para analisar binários. Um conjunto adequado de binários pré-construídos vem com o kernel-build-tools e é usado automaticamente com build_abi.sh
.
Para utilizar as ferramentas de nível inferior (como dump_abi
), adicione as ferramentas kernel-build- ao PATH
.
3. Construa o kernel e sua representação ABI
Neste ponto, você está pronto para construir um kernel com a cadeia de ferramentas correta e extrair uma representação ABI de seus binários ( vmlinux
+ módulos).
Semelhante ao processo normal de compilação do kernel do Android (usando build.sh
), esta etapa requer a execução de build_abi.sh
.
BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh
Isso cria o kernel e extrai a representação ABI no subdiretório out_abi
. Neste caso out/android12-5.10/dist/abi.xml
é um link simbólico para out_abi/android12-5.10/dist/abi-<id>.xml
. < id>
é calculado executando git describe
na árvore de origem do kernel.
4. Analise as diferenças de ABI entre a construção e uma representação de referência
build_abi.sh
analisa e relata quaisquer diferenças de ABI quando uma referência é fornecida por meio da variável de ambiente ABI_DEFINITION
. ABI_DEFINITION
deve apontar para um arquivo de referência relativo à árvore de origem do kernel e pode ser especificado na linha de comando ou, mais comumente, como um valor em build.config . O seguinte fornece um exemplo:
BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh
No comando acima, build.config.gki.aarch64
define o arquivo de referência (como ABI_DEFINITION=android/abi_gki_aarch64.xml
), e diff_abi
chama abidiff
para comparar a representação ABI recém-gerada com o arquivo de referência. build_abi.sh
imprime a localização do relatório e emite um breve relatório para qualquer quebra de ABI. Se forem detectadas quebras, build_abi.sh
termina e retorna um código de saída diferente de zero.
5. Atualize a representação da ABI (se necessário)
Para atualizar a representação ABI, invoque build_abi.sh
com o sinalizador --update
. Ele atualiza o arquivo abi.xml
correspondente definido por build.config
. Para imprimir as diferenças de ABI devido à atualização, invoque o script com --print-report
. Certifique-se de incluir o relatório na mensagem de confirmação ao atualizar o arquivo abi.xml
.
6. Trabalhe com listas de símbolos
Parametrize build_abi.sh
com listas de símbolos KMI para filtrar símbolos durante a extração de ABI. Estes são arquivos de texto simples que listam os símbolos relevantes do kernel ABI. Por exemplo, um arquivo de lista de símbolos com o conteúdo a seguir limita a análise ABI aos símbolos ELF com os nomes symbol1
e symbol2
:
[abi_symbol_list]
symbol1
symbol2
Alterações em outros símbolos ELF não são consideradas. Um arquivo de lista de símbolos pode ser especificado no arquivo de configuração build.config
correspondente com KMI_SYMBOL_LIST=
como um arquivo relativo ao diretório de origem do kernel ( $KERNEL_DIR
). Para fornecer um nível de organização, você pode especificar arquivos de lista de símbolos adicionais usando ADDITIONAL_KMI_SYMBOL_LISTS=
no arquivo build.config
. Isso especifica outros arquivos de lista de símbolos, relativos a $KERNEL_DIR
; separe vários nomes de arquivos por espaço em branco.
Para criar uma lista de símbolos inicial ou atualizar uma existente , você deve usar o script build_abi.sh
com o --update-symbol-list
.
Quando o script é executado com uma configuração apropriada, ele constrói o kernel e extrai os símbolos que são exportados dos módulos vmlinux
e GKI e que são requeridos por qualquer outro módulo na árvore.
Considere vmlinux
exportando os seguintes símbolos (geralmente feito por meio das macros EXPORT_SYMBOL*
):
func1
func2
func3
Além disso, imagine que houvesse dois módulos de fornecedor, modA.ko
e modB.ko
, que exigem os seguintes símbolos (em outras palavras, eles listam entradas de símbolos undefined
em sua tabela de símbolos):
modA.ko: func1 func2
modB.ko: func2
Do ponto de vista da estabilidade da ABI, func1
e func2
devem ser mantidos estáveis, pois são usados por um módulo externo. Pelo contrário, enquanto func3
é exportado, ele não é usado ativamente (ou seja, não é necessário) por nenhum módulo. Assim, a lista de símbolos contém apenas func1
e func2
.
Para criar ou atualizar uma lista de símbolos existente, build_abi.sh
deve ser executado da seguinte forma:
BUILD_CONFIG=path/to/build.config.device build/build_abi.sh --update-symbol-list
Neste exemplo, build.config.device
deve incluir várias opções de configuração:
-
vmlinux
deve estar na listaFILES
. -
KMI_SYMBOL_LIST
deve ser definido e apontando para a lista de símbolos KMI para atualizar. -
GKI_MODULES_LIST
deve ser definido e apontando para a lista de módulos GKI. Esse caminho geralmente éandroid/gki_aarch64_modules
.
Trabalhe com as ferramentas ABI de nível inferior
A maioria dos usuários só precisará usar build_abi.sh
. Em alguns casos, pode ser necessário trabalhar diretamente com as ferramentas ABI de nível inferior. Os dois comandos usados por build_abi.sh
, dump_abi
e diff_abi
, estão disponíveis para extrair e comparar arquivos ABI. Consulte as seções a seguir para seus usos.
Crie representações ABI a partir de árvores do kernel
Fornecendo uma árvore de kernel linux com módulos vmlinux
e kernel construídos, a ferramenta dump_abi
cria uma representação ABI usando a ferramenta ABI selecionada. Uma invocação de amostra se parece com isso:
dump_abi --linux-tree path/to/out --out-file /path/to/abi.xml
O arquivo abi.xml
contém uma representação textual ABI da ABI combinada e observável do vmlinux
e dos módulos do kernel no diretório fornecido. Esse arquivo pode ser usado para inspeção manual, análise adicional ou como um arquivo de referência para reforçar a estabilidade da ABI.
Comparar representações ABI
As representações ABI criadas por dump_abi
podem ser comparadas com diff_abi
. Use a mesma ferramenta abi para dump_abi
e diff_abi
. Uma invocação de amostra se parece com isso:
diff_abi --baseline abi1.xml --new abi2.xml --report report.out
O relatório gerado lista as alterações de ABI detectadas que afetam o KMI. Os arquivos especificados como baseline
e new
são representações ABI que foram coletadas com dump_abi
. diff_abi
propaga o código de saída da ferramenta subjacente e, portanto, retorna um valor diferente de zero quando as ABIs comparadas são incompatíveis.
Filtrar representações e símbolos KMI
Para filtrar representações criadas com dump_abi
ou para filtrar símbolos comparados com diff_abi
, use o parâmetro --kmi-symbol-list
, que leva um caminho para um arquivo de lista de símbolos KMI:
dump_abi --linux-tree path/to/out --out-file /path/to/abi.xml --kmi-symbol-list /path/to/symbol_list_file
Trabalhando com listas de símbolos
O KMI não inclui todos os símbolos no kernel ou mesmo todos os mais de 30.000 símbolos exportados. Em vez disso, os símbolos que podem ser usados pelos módulos são listados explicitamente em um conjunto de arquivos de lista de símbolos mantidos publicamente na raiz da árvore do kernel. A união de todos os símbolos em todos os arquivos de lista de símbolos define o conjunto de símbolos KMI mantidos como estáveis. Um exemplo de arquivo de lista de símbolos é abi_gki_aarch64_db845c , que declara os símbolos necessários para o DragonBoard 845c .
Apenas os símbolos listados em uma lista de símbolos e suas estruturas e definições relacionadas são considerados parte do KMI. Você pode postar alterações em suas listas de símbolos se os símbolos necessários não estiverem presentes. Depois que as novas interfaces estão em uma lista de símbolos e fazem parte da descrição do KMI, elas são mantidas estáveis e não devem ser removidas da lista de símbolos ou modificadas após o congelamento da ramificação.
Cada ramificação do kernel KMI do Android Common Kernel (ACK) tem seu próprio conjunto de listas de símbolos. Nenhuma tentativa é feita para fornecer estabilidade de ABI entre diferentes ramificações do kernel KMI. Por exemplo, o KMI para android12-5.10
é completamente independente do KMI para android13-5.10
.
As ferramentas ABI usam listas de símbolos KMI para limitar quais interfaces devem ser monitoradas para estabilidade. A lista de símbolos principal contém os símbolos que são requeridos pelos módulos do kernel GKI. Espera-se que os fornecedores enviem e atualizem listas de símbolos adicionais para garantir que as interfaces nas quais eles dependem mantenham a compatibilidade com ABI. Por exemplo, para ver uma lista de listas de símbolos para o android13-5.15
, consulte https://android.googlesource.com/kernel/common/+/refs/heads/android13-5.15/android
Uma lista de símbolos contém os símbolos relatados como necessários para o fornecedor ou dispositivo específico. A lista completa utilizada pelas ferramentas é a união de todos os arquivos da lista de símbolos KMI. As ferramentas ABI determinam os detalhes de cada símbolo, incluindo assinatura de função e estruturas de dados aninhadas.
Quando o KMI está congelado, nenhuma alteração é permitida nas interfaces KMI existentes; eles são estáveis. No entanto, os fornecedores são livres para adicionar símbolos ao KMI a qualquer momento, desde que as adições não afetem a estabilidade da ABI existente. Os símbolos recém-adicionados são mantidos estáveis assim que são citados por uma lista de símbolos KMI. Símbolos não devem ser removidos de uma lista de um kernel a menos que possa ser confirmado que nenhum dispositivo foi enviado com uma dependência desse símbolo.
Você pode gerar uma lista de símbolos KMI para um dispositivo usando o utilitário build/abi/extract_symbols
, que extrai as dependências de símbolos dos artefatos de compilação *.ko
. Este utilitário adiciona anotações à saída na forma de comentários, que são úteis para identificar usuários de um símbolo. Ao enviar a lista de símbolos para ACK, é altamente recomendável manter esses comentários para facilitar o processo de revisão. Para omitir comentários, passe a --skip-module-grouping
ao executar o script extract_symbols
. Muitos parceiros enviam uma lista de símbolos por ACK, mas isso não é um requisito difícil. Se isso ajudar na manutenção, você pode enviar várias listas de símbolos.
Para obter ajuda com a personalização de listas de símbolos e o uso de ferramentas ABI de alto e baixo nível para depuração e análise detalhada, consulte Monitoramento de ABI para kernels do Android .
Estenda o KMI
Embora os símbolos KMI e as estruturas relacionadas sejam mantidos estáveis (o que significa que alterações que quebram interfaces estáveis em um kernel com um KMI congelado não podem ser aceitas), o kernel GKI permanece aberto a extensões para que os dispositivos enviados no final do ano não precisem defina todas as suas dependências antes que o KMI seja congelado. Para estender o KMI, você pode adicionar novos símbolos ao KMI para funções de kernel exportadas novas ou existentes, mesmo se o KMI estiver congelado. Novos patches de kernel também são aceitos se não quebrarem o KMI.
Sobre quebras de KMI
Um kernel tem fontes e um binário é construído com base nessas fontes. As ramificações do kernel monitoradas por ABI incluem abi.xml
que é uma representação da GKI ABI atual. Depois que o binário é compilado (o binário do kernel, vmlinux
, Image
mais módulos do kernel), um arquivo abi.xml
pode ser extraído dos binários. Qualquer alteração feita em uma fonte do kernel pode alterar o binário e também pode alterar o abi.xml
extraído (o arquivo que é extraído após aplicar a alteração e construir o kernel). O analisador AbiAnalyzer
compara os dois arquivos abi.xml
semanticamente e define um rótulo Lint-1 na alteração se encontrar um problema.
Lidar com quebras de ABI
Como exemplo, o patch a seguir apresenta uma quebra de ABI muito óbvia:
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 5ed8f6292a53..f2ecb34c7645 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -339,6 +339,7 @@ struct core_state {
struct kioctx_table;
struct mm_struct {
struct {
+ int dummy;
struct vm_area_struct *mmap; /* list of VMAs */
struct rb_root mm_rb;
u64 vmacache_seqnum; /* per-thread vmacache */
Quando você executa o build_abi.sh
novamente com este patch aplicado, o conjunto de ferramentas sai com um código de erro diferente de zero e relata uma diferença de ABI semelhante a esta:
Leaf changes summary: 1 artifact changed
Changed leaf types summary: 1 leaf type changed
Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 0 Added function
Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable
'struct mm_struct at mm_types.h:372:1' changed:
type size changed from 6848 to 6912 (in bits)
there are data member changes:
[...]
Diferenças de ABI detectadas em tempo de compilação
O motivo mais comum para erros é quando um driver usa um novo símbolo do kernel que não está em nenhuma das listas de símbolos.
Se o símbolo não estiver incluído na lista de símbolos ( android/abi_gki_aarch64
), você precisará primeiro verificar se ele foi exportado com EXPORT_SYMBOL_GPL( symbol_name )
e, em seguida, atualizar a representação XML da ABI e a lista de símbolos. Por exemplo, as alterações a seguir adicionam o novo recurso Incremental FS à ramificação android-12-5.10
, que inclui a atualização da lista de símbolos e a representação XML da ABI.
- O exemplo de alteração de recurso está em aosp/1345659 .
- O exemplo da lista de símbolos está em aosp/1346742 .
- O exemplo de alteração do XML da ABI está em aosp/1349377 .
Se o símbolo for exportado (por você ou foi exportado anteriormente), mas nenhum outro driver o estiver usando no momento, você poderá receber um erro de compilação semelhante ao seguinte.
Comparing the KMI and the symbol lists:
+ build/abi/compare_to_symbol_list out/$BRANCH/common/Module.symvers out/$BRANCH/common/abi_symbollist.raw
ERROR: Differences between ksymtab and symbol list detected!
Symbols missing from ksymtab:
Symbols missing from symbol list:
- simple_strtoull
Para resolver, atualize a lista de símbolos KMI em seu kernel e no ACK (consulte Atualizar a representação da ABI ). Para obter um exemplo de atualização do XML ABI e da lista de símbolos no ACK, consulte aosp/1367601 .
Resolver quebras de ABI do kernel
Você pode lidar com quebras de ABI do kernel refatorando o código para não alterar a ABI ou atualizando a representação da ABI . Use o gráfico a seguir para determinar a melhor abordagem para sua situação.
Figura 1. Resolução de quebra de ABI
Refatorar código para evitar alterações de ABI
Faça todos os esforços para evitar modificar a ABI existente. Em muitos casos, você pode refatorar seu código para remover as alterações que afetam a ABI.
Refatorando alterações de campo de estrutura. Se uma alteração modifica a ABI para um recurso de depuração, adicione um
#ifdef
ao redor dos campos (nas estruturas e referências de origem) e certifique-se de que oCONFIG
usado para o#ifdef
esteja desabilitado para a produção defconfig egki_defconfig
. Para obter um exemplo de como uma configuração de depuração pode ser adicionada a um struct sem quebrar a ABI, consulte este conjunto de patches .Recursos de refatoração para não alterar o kernel principal. Se novos recursos precisarem ser adicionados ao ACK para oferecer suporte aos módulos parceiros, tente refatorar a parte ABI da alteração para evitar modificar a ABI do kernel. Para obter um exemplo de uso da ABI do kernel existente para adicionar funcionalidades adicionais sem alterar a ABI do kernel, consulte aosp/1312213 .
Corrigir uma ABI quebrada no Android Gerrit
Se você não quebrou intencionalmente a ABI do kernel, precisará investigar, usando a orientação fornecida pelas ferramentas de monitoramento da ABI. As causas mais comuns de quebras são funções adicionadas ou excluídas, estruturas de dados alteradas ou alterações na ABI causadas pela adição de opções de configuração que levam a qualquer um dos itens mencionados acima. Comece abordando os problemas encontrados pela ferramenta.
Você pode reproduzir o teste ABI localmente executando o seguinte comando com os mesmos argumentos que você usaria para executar build/build.sh
:
Este é um comando de exemplo para os kernels GKI:
BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh
Sobre rótulos Lint-1
Se você carregar alterações em uma ramificação contendo um KMI congelado ou finalizado, as alterações devem passar pelo ABIAnalyzer
para garantir que as alterações não afetem a ABI estável de maneira incompatível. Durante esse processo, o ABIAnalyzer
procura o relatório ABI que é criado durante a compilação (uma compilação estendida que executa a compilação normal e, em seguida, algumas etapas de extração e comparação de ABI. Se o ABIAnalyzer
encontrar um relatório não vazio, ele definirá o rótulo Lint-1 e a alteração é bloqueada do envio até ser resolvida; até que o conjunto de patches receba um rótulo Lint+1.
Atualizar a ABI do Kernel
Se você precisar atualizar a representação da ABI do kernel, deverá atualizar o arquivo abi.xml
correspondente na árvore de origem do kernel. A maneira mais conveniente de fazer isso é usando build/build_abi.sh
assim:
build/build_abi.sh --update --print-report
Use os mesmos argumentos que você usaria para executar build/build.sh
. Isso atualiza o abi.xml
correto na árvore de origem e imprime as diferenças detectadas. Por uma questão de prática, inclua o relatório impresso (curto) na mensagem de confirmação (pelo menos parcialmente).
Atualizar a representação ABI
Se a modificação da ABI for inevitável, seu código será alterado e o XML da ABI e a lista de símbolos precisam ser aplicados ao ACK. Para que o Lint remova o -1 e não quebre a compatibilidade do GKI, execute as seguintes etapas:
Mescle suas alterações de código e a alteração de atualização da ABI.
Carregar alterações de código ABI para o ACK
A atualização da ACK ABI depende do tipo de alteração que está sendo feita.
Se uma alteração de ABI estiver relacionada a um recurso que afeta os testes CTS ou VTS, a alteração geralmente pode ser selecionada para ACK como está. Por exemplo:
- aosp/1289677 é necessário para que o áudio funcione.
- aosp/1295945 é necessário para que o USB funcione.
Se uma alteração de ABI for para um recurso que pode ser compartilhado com o ACK, essa alteração pode ser selecionada para ACK como está. Por exemplo, as seguintes alterações não são necessárias para o teste CTS ou VTS, mas podem ser compartilhadas com ACK:
- aosp/1250412 é uma alteração de recurso térmico.
- aosp/1288857 é uma alteração
EXPORT_SYMBOL_GPL
.
Se uma alteração de ABI introduzir um novo recurso que não precisa ser incluído no ACK, você poderá introduzir os símbolos no ACK usando um stub conforme descrito na seção a seguir.
Use stubs para ACK
Os stubs devem ser necessários apenas para alterações do núcleo do kernel que não beneficiam o ACK, como alterações de desempenho e energia. A lista a seguir detalha exemplos de stubs e seleções parciais em ACK para GKI.
Esboço de recurso de isolamento de núcleo ( aosp/1284493 ). A funcionalidade no ACK não é necessária, mas os símbolos precisam estar presentes no ACK para que seus módulos usem esses símbolos.
Símbolo de espaço reservado para o módulo do fornecedor ( aosp/1288860 ).
Escolha apenas ABI do recurso de rastreamento de eventos
mm
por processo ( aosp/1288454 ). O patch original foi escolhido a dedo para ACK e depois cortado para incluir apenas as alterações necessárias para resolver o diff ABI paratask_struct
emm_event_count
. Este patch também atualiza o enummm_event_type
para conter os membros finais.Seleção parcial de alterações da ABI da estrutura térmica que exigiam mais do que apenas adicionar os novos campos da ABI.
O patch aosp/1255544 resolveu as diferenças de ABI entre o kernel parceiro e o ACK.
O patch aosp/1291018 corrigiu os problemas funcionais encontrados durante o teste GKI do patch anterior. A correção incluiu a inicialização da estrutura de parâmetros do sensor para registrar várias zonas térmicas em um único sensor.
CONFIG_NL80211_TESTMODE
ABI altera ( aosp/1344321 ). Esse patch adicionou as alterações de estrutura necessárias para ABI e garantiu que os campos adicionais não causassem diferenças funcionais, permitindo que os parceiros incluíssemCONFIG_NL80211_TESTMODE
em seus kernels de produção e ainda mantivessem a conformidade com GKI.
Atualizar arquivos ACK ABI
Para atualizar os arquivos ACK ABI:
Carregue as alterações da ABI e espere receber um Code-Review +2 para o conjunto de patches.
Atualize os arquivos ACK ABI.
cp partner kernel/android/abi_gki_aarch64_partner ACK kernel/abi_gki_aarch64_partner
BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh --update
Confirme a atualização da ABI:
cd common
git add android/abi*
git commit -s -F $DIST_DIR/abi.report.short
<push to gerrit>
$DIST_DIR/abi.report.short
contém um breve relatório das mudanças. Usar o sinalizador-F
comgit commit
usa automaticamente o relatório para o texto do commit, que você pode editar para adicionar uma linha de assunto (ou aparar se a mensagem for muito longa).
Ramificações do kernel do Android com ABI predefinida
Algumas ramificações do kernel vêm com representações ABI predefinidas para Android como parte de sua distribuição de origem. Essas representações ABI devem ser precisas e refletir o resultado de build_abi.sh
como se você fosse executá-lo por conta própria. Como a ABI é fortemente influenciada por várias opções de configuração do kernel, esses arquivos .xml
geralmente pertencem a uma determinada configuração. Por exemplo, a ramificação common-android12-5.10
contém um abi_gki_aarch64.xml
que corresponde ao resultado da compilação ao usar o build.config.gki.aarch64
. Em particular, build.config.gki.aarch64
também se refere a esse arquivo por meio de ABI_DEFINITION
.
Essas representações ABI predefinidas são usadas como uma definição de linha de base ao comparar com diff_abi
. Por exemplo, para validar um patch do kernel em relação a quaisquer alterações na ABI, crie a representação da ABI com o patch aplicado e use diff_abi
para compará-la com a ABI esperada para essa árvore ou configuração de origem específica. Se ABI_DEFINITION
estiver definido, a execução de build_abi.sh
será suficiente.
Aplicar o KMI em tempo de execução
Os kernels GKI usam as opções de configuração TRIM_UNUSED_KSYMS=y
e UNUSED_KSYMS_WHITELIST=<union of all symbol lists>
, que limitam os símbolos exportados (como símbolos exportados usando EXPORT_SYMBOL_GPL()
) aos listados em uma lista de símbolos. Todos os outros símbolos não são exportados e o carregamento de um módulo que requer um símbolo não exportado é negado. Essa restrição é aplicada no momento da compilação e as entradas ausentes são sinalizadas.
Para fins de desenvolvimento, você pode usar uma compilação do kernel GKI que não inclui corte de símbolos (o que significa que todos os símbolos normalmente exportados podem ser usados). Para localizar essas compilações, procure as compilações kernel_debug_aarch64
em ci.android.com .
Aplicar o KMI usando o controle de versão do módulo
Os kernels Generic Kernel Image (GKI) usam o controle de versão do módulo ( CONFIG_MODVERSIONS
) como uma medida adicional para impor a conformidade com KMI em tempo de execução. O controle de versão do módulo pode causar falhas de incompatibilidade de verificação de redundância cíclica (CRC) no tempo de carregamento do módulo se o KMI esperado de um módulo não corresponder ao KMI vmlinux
. Por exemplo, o seguinte é uma falha típica que ocorre no tempo de carregamento do módulo devido a uma incompatibilidade de CRC para o símbolo module_layout()
:
init: Loading module /lib/modules/kernel/.../XXX.ko with args ""
XXX: disagrees about version of symbol module_layout
init: Failed to insmod '/lib/modules/kernel/.../XXX.ko' with args ''
Usos de versionamento de módulo
O controle de versão do módulo é útil pelos seguintes motivos:
O controle de versão do módulo captura as alterações na visibilidade da estrutura de dados. Se os módulos alterarem estruturas de dados opacas, ou seja, estruturas de dados que não fazem parte do KMI, eles serão interrompidos após alterações futuras na estrutura.
Como exemplo, considere o campo
fwnode
emstruct device
. Este campo DEVE ser opaco aos módulos para que eles não possam fazer alterações nos campos dedevice->fw_node
ou fazer suposições sobre seu tamanho.No entanto, se um módulo incluir
<linux/fwnode.h>
(direta ou indiretamente), o campofwnode
nostruct device
não será mais opaco para ele. O módulo pode então fazer alterações emdevice->fwnode->dev
oudevice->fwnode->ops
. Esse cenário é problemático por várias razões, conforme indicado a seguir:Ele pode quebrar suposições que o código do núcleo do kernel está fazendo sobre suas estruturas de dados internas.
Se uma atualização futura do kernel alterar o
struct fwnode_handle
(o tipo de dados defwnode
), o módulo não funcionará mais com o novo kernel. Além disso,abidiff
não mostrará nenhuma diferença porque o módulo está quebrando o KMI manipulando diretamente as estruturas de dados internas de maneiras que não podem ser capturadas apenas inspecionando a representação binária.
Um módulo atual é considerado incompatível com KMI quando é carregado posteriormente por um novo kernel incompatível. O controle de versão do módulo adiciona uma verificação em tempo de execução para evitar o carregamento acidental de um módulo que não é compatível com KMI com o kernel. Essa verificação evita problemas de tempo de execução difíceis de depurar e travamentos do kernel que podem resultar de uma incompatibilidade não detectada no KMI.
abidiff
tem limitações na identificação de diferenças de ABI em certos casos complicados queCONFIG_MODVERSIONS
pode capturar.
Ativar o controle de versão do módulo evita todos esses problemas.
Verifique se há incompatibilidades de CRC sem inicializar o dispositivo
abidiff
compara e relata incompatibilidades de CRC entre os kernels. Esta ferramenta permite que você capture CRC incompatíveis ao mesmo tempo que outras diferenças de ABI.
Além disso, uma compilação completa do kernel com CONFIG_MODVERSIONS
habilitado gera um arquivo Module.symvers
como parte do processo normal de compilação. Este arquivo possui uma linha para cada símbolo exportado pelo kernel ( vmlinux
) e pelos módulos. Cada linha consiste no valor CRC, nome do símbolo, namespace do símbolo, vmlinux
ou nome do módulo que está exportando o símbolo e o tipo de exportação (por exemplo, EXPORT_SYMBOL
versus EXPORT_SYMBOL_GPL
).
Você pode comparar os arquivos Module.symvers
entre a compilação GKI e sua compilação para verificar quaisquer diferenças de CRC nos símbolos exportados pelo vmlinux
. Se houver uma diferença de valor CRC em qualquer símbolo exportado pelo vmlinux
e esse símbolo for usado por um dos módulos que você carrega em seu dispositivo, o módulo não carrega.
Se você não tiver todos os artefatos de compilação, mas tiver os arquivos vmlinux
do kernel GKI e seu kernel, poderá comparar os valores de CRC para um símbolo específico executando o seguinte comando em ambos os kernels e comparando a saída:
nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>
Por exemplo, o comando a seguir verifica o valor CRC para o símbolo module_layout
:
nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout
Resolver incompatibilidades de CRC
Use as etapas a seguir para resolver uma incompatibilidade de CRC ao carregar um módulo:
Compile o kernel GKI e o kernel do seu dispositivo
KBUILD_SYMTYPES=1
ao comando que você usa para compilar o kernel, conforme mostrado no comando a seguir:KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
Este comando gera um arquivo
.symtypes
para cada arquivo.o
. Ao usarbuild_abi.sh,
oKBUILD_SYMTYPES=1
já está definido implicitamente.Encontre o arquivo
.c
no qual o símbolo com incompatibilidade de CRC é exportado, usando o seguinte comando:cd common && git grep EXPORT_SYMBOL.*module_layout kernel/module.c:EXPORT_SYMBOL(module_layout);
O arquivo
.c
tem um arquivo.symtypes
correspondente no GKI e os artefatos de compilação do kernel do seu dispositivo. Localize o arquivo.c
usando os seguintes comandos:cd out/$BRANCH/common && ls -1 kernel/module.* kernel/module.o kernel/module.o.symversions kernel/module.symtypes
A seguir estão as características do arquivo
.c
:O formato do arquivo
.c
é uma linha (potencialmente muito longa) por símbolo.[s|u|e|etc]#
no início da linha significa que o símbolo é do tipo de dados[struct|union|enum|etc]
. Por exemplo:t#bool typedef _Bool bool
Um prefixo
#
ausente no início da linha indica que o símbolo é uma função. Por exemplo:find_module s#module * find_module ( const char * )
Compare os dois arquivos e corrija todas as diferenças.
Caso 1: Diferenças devido à visibilidade do tipo de dados
Se um kernel mantém um símbolo ou tipo de dados opaco para os módulos e o outro kernel não, essa diferença aparece entre os arquivos .symtypes
dos dois kernels. O arquivo .symtypes
de um dos kernels tem um símbolo UNKNOWN
e o arquivo .symtypes
do outro kernel tem uma visão expandida do símbolo ou tipo de dados.
Por exemplo, adicionar a seguinte linha ao arquivo include/linux/device.h
em seu kernel causa incompatibilidades de CRC, uma das quais é para module_layout()
:
#include <linux/fwnode.h>
Comparando o module.symtypes
para esse símbolo, expõe as seguintes diferenças:
$ diff -u <GKI>/kernel/module.symtypes <your kernel>/kernel/module.symtypes
--- <GKI>/kernel/module.symtypes
+++ <your kernel>/kernel/module.symtypes
@@ -334,12 +334,15 @@
...
-s#fwnode_handle struct fwnode_handle { UNKNOWN }
+s#fwnode_reference_args struct fwnode_reference_args { s#fwnode_handle * fwnode ; unsigned int nargs ; t#u64 args [ 8 ] ; }
...
Se o seu kernel tiver um valor de UNKNOWN
e o kernel GKI tiver a visualização expandida do símbolo (muito improvável), mescle o kernel comum do Android mais recente em seu kernel para que você esteja usando a base do kernel GKI mais recente.
Na maioria dos casos, o kernel GKI tem um valor de UNKNOWN
, mas seu kernel tem os detalhes internos do símbolo devido às alterações feitas em seu kernel. Isso ocorre porque um dos arquivos em seu kernel adicionou um #include
que não está presente no kernel GKI.
Para identificar o #include
que causa a diferença, siga estas etapas:
Abra o arquivo de cabeçalho que define o símbolo ou tipo de dados com essa diferença. Por exemplo, edite
include/linux/fwnode.h
para ostruct fwnode_handle
.Adicione o seguinte código na parte superior do arquivo de cabeçalho:
#ifdef CRC_CATCH #error "Included from here" #endif
No arquivo
.c
do módulo que tem uma incompatibilidade de CRC, adicione o seguinte como a primeira linha antes de qualquer uma das linhas#include
.#define CRC_CATCH 1
Compile seu módulo. O erro de tempo de compilação resultante mostra a cadeia do arquivo de cabeçalho
#include
que levou a essa incompatibilidade de CRC. Por exemplo:In file included from .../drivers/clk/XXX.c:16:` In file included from .../include/linux/of_device.h:5: In file included from .../include/linux/cpu.h:17: In file included from .../include/linux/node.h:18: .../include/linux/device.h:16:2: error: "Included from here" #error "Included from here"
Um dos elos dessa corrente de
#include
se deve a uma alteração feita em seu kernel, que está faltando no kernel GKI.Identifique a mudança, reverta-a em seu kernel ou carregue-a para ACK e faça a mesclagem .
Caso 2: Diferenças devido a alterações de tipo de dados
Se a incompatibilidade de CRC para um símbolo ou tipo de dados não for devido a uma diferença na visibilidade, isso se deve a alterações reais (adições, remoções ou alterações) no próprio tipo de dados. Normalmente, abidiff
captura isso, mas se perder algum devido a falhas de detecção conhecidas, o mecanismo MODVERSIONS
pode capturá-los.
Por exemplo, fazer a seguinte alteração em seu kernel causa várias incompatibilidades de CRC, pois muitos símbolos são afetados indiretamente por esse tipo de alteração:
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -259,7 +259,7 @@ struct iommu_ops {
void (*iotlb_sync)(struct iommu_domain *domain);
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
- dma_addr_t iova);
+ dma_addr_t iova, unsigned long trans_flag);
int (*add_device)(struct device *dev);
void (*remove_device)(struct device *dev);
struct iommu_group *(*device_group)(struct device *dev);
Uma incompatibilidade de CRC é para devm_of_platform_populate()
.
Se você comparar os arquivos .symtypes
para esse símbolo, pode ficar assim:
$ diff -u <GKI>/drivers/of/platform.symtypes <your kernel>/drivers/of/platform.symtypes
--- <GKI>/drivers/of/platform.symtypes
+++ <your kernel>/drivers/of/platform.symtypes
@@ -399,7 +399,7 @@
...
-s#iommu_ops struct iommu_ops { ... ; t#phy
s_addr_t ( * iova_to_phys_hard ) ( s#iommu_domain * , t#dma_addr_t ) ; int
( * add_device ) ( s#device * ) ; ...
+s#iommu_ops struct iommu_ops { ... ; t#phy
s_addr_t ( * iova_to_phys_hard ) ( s#iommu_domain * , t#dma_addr_t , unsigned long ) ; int ( * add_device ) ( s#device * ) ; ...
Para identificar o tipo alterado, siga estas etapas:
Encontre a definição do símbolo no código-fonte (geralmente em arquivos
.h
).- Para diferenças de símbolos simples entre seu kernel e o kernel GKI, encontre o commit executando o seguinte comando:
git blame
- Para símbolos excluídos (onde um símbolo é excluído em uma árvore e você também deseja excluí-lo na outra árvore), você precisa encontrar a alteração que excluiu a linha. Use o seguinte comando na árvore em que a linha foi excluída:
git log -S "copy paste of deleted line/word" -- <file where it was deleted>
Revise a lista retornada de confirmações para localizar a alteração ou exclusão. O primeiro commit é provavelmente aquele que você está procurando. Se não for, percorra a lista até encontrar o commit.
Depois de identificar a alteração, reverta-a em seu kernel ou carregue-a no ACK e faça a mesclagem .