Você pode usar as ferramentas de monitoramento da interface binária de aplicativo (ABI, na sigla em inglês), disponíveis no
Android 11 e versões mais recentes, para estabilizar a ABI
no kernel de kernels do Android. As ferramentas coletam e comparam representações de ABI
de binários do kernel atuais (vmlinux
+ módulos de GKI). Essas representações de ABI são os arquivos .stg
e as listas de símbolos. A interface em que a representação oferece uma visualização é chamada de Interface do módulo do kernel (KMI, na sigla em inglês). É possível usar as ferramentas para rastrear e reduzir as mudanças no KMI.
As ferramentas de monitoramento de ABI são
desenvolvidas no AOSP
e usam
STG (ou
libabigail
no
Android 13 e versões anteriores) 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 dessas representações para oferecer estabilidade à ABI no kernel. Esta página também fornece informações sobre como contribuir com mudanças para os kernels do Android.
Processo
A análise da ABI do kernel envolve várias etapas, a maioria delas automatizada:
- Crie o kernel e a representação da ABI dele.
- Analisar as diferenças de ABI entre o build e uma referência.
- Atualize a representação da ABI (se necessário).
- Trabalhar com listas de símbolos.
As instruções a seguir funcionam para qualquer
kernel que você possa criar usando uma
cadeia de ferramentas compatível (como a cadeia de ferramentas Clang pré-criada). repo manifests
estão disponíveis para todas as ramificações comuns do kernel do Android e para vários
kernels específicos do dispositivo. Eles verificam se a cadeia de ferramentas correta é usada quando você
cria uma distribuição do kernel para análise.
Listas de símbolos
O KMI não inclui todos os símbolos no kernel nem todos os mais de 30.000 símbolos exportados. Em vez disso, os símbolos que podem ser usados pelos módulos do fornecedor são
listados explicitamente em um conjunto de arquivos de lista de símbolos mantidos publicamente na
árvore do kernel (gki/{ARCH}/symbols/*
ou android/abi_gki_{ARCH}_*
no Android 15
e versões anteriores). A união de todos os símbolos em todos os arquivos de lista de símbolos
define o conjunto de símbolos de KMI mantidos como estáveis. Um exemplo de arquivo de lista de símbolos é
gki/aarch64/symbols/db845c
,
que declara os símbolos necessários para o
DragonBoard 845c.
Somente os símbolos listados em uma lista de símbolos e as estruturas e definições relacionadas a eles são considerados parte da KMI. Você pode postar mudanças nas 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 da KMI, elas são mantidas como estáveis e não podem ser removidas da lista de símbolos nem modificadas depois que a ramificação é congelada.
Cada ramificação do kernel KMI do kernel comum do Android (ACK) tem seu próprio conjunto de listas de símbolos. Não há tentativa de fornecer estabilidade de ABI entre diferentes ramificações do kernel
KMI. Por exemplo, o KMI de android12-5.10
é completamente independente do KMI de android13-5.10
.
As ferramentas de ABI usam listas de símbolos da KMI para limitar quais interfaces precisam ser monitoradas para
estabilidade. Os fornecedores precisam enviar e atualizar as próprias listas de símbolos para verificar se as interfaces em que se baseiam mantêm a compatibilidade com a ABI. Por exemplo, para ver uma lista de listas de símbolos do kernel android16-6.12
, consulte
https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols
Uma lista de símbolos contém os símbolos que precisam ser informados para o fornecedor ou dispositivo específico. A lista completa usada pelas ferramentas é a união de todos os arquivos de lista de símbolos do KMI. As ferramentas de ABI determinam os detalhes de cada símbolo, incluindo assinatura de função e estruturas de dados aninhadas.
Quando a KMI é congelada, não são permitidas mudanças nas interfaces dela. Elas ficam estáveis. No entanto, os fornecedores podem adicionar símbolos à KMI a qualquer momento, desde que as adições não afetem a estabilidade da ABI atual. Os símbolos recém-adicionados são mantidos estáveis assim que são citados por uma lista de símbolos do KMI. Os símbolos não podem ser removidos de uma lista para um kernel, a menos que seja possível confirmar que nenhum dispositivo foi enviado com uma dependência desse símbolo.
Para gerar uma lista de símbolos do KMI para um dispositivo, siga as instruções em Como trabalhar com listas de símbolos. Muitos parceiros enviam uma lista de símbolos por ACK, mas isso não é um requisito obrigatório. Se isso ajudar na manutenção, envie várias listas de símbolos.
Estender o KMI
Embora os símbolos da KMI e as estruturas relacionadas sejam mantidos como estáveis (ou seja, não é possível aceitar mudanças que quebrem interfaces estáveis em um kernel com uma KMI congelada), o kernel GKI permanece aberto a extensões para que os dispositivos lançados mais tarde no ano não precisem definir todas as dependências antes do congelamento da KMI. Para estender a KMI, adicione novos símbolos a ela para funções de kernel exportadas novas ou existentes, mesmo que a KMI esteja congelada. Novos patches de kernel também podem ser aceitos se não violarem a KMI.
Sobre falhas na KMI
Um kernel tem fontes, e os binários são criados com base nelas.
As ramificações do kernel monitoradas por ABI incluem uma representação da ABI GKI atual (na forma de um arquivo .stg
). Depois que os binários (vmlinux
, Image
e
todos os módulos de GKI) são criados, uma representação de ABI pode ser extraída dos
binários. Qualquer mudança feita em um arquivo de origem do kernel pode afetar os binários e, por
sua vez, também afetar o .stg
extraído. A análise de conformidade da ABI compara o arquivo .stg
confirmado com o extraído dos artefatos de build e define um
rótulo Lint-1 na mudança no Gerrit se encontrar uma diferença semântica.
Processar quebras de ABI
Por 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 42786e6364ef..e15f1d0f137b 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -657,6 +657,7 @@ struct mm_struct {
ANDROID_KABI_RESERVE(1);
} __randomize_layout;
+ int tickle_count;
/*
* The mm_cpumask needs to be at the end of mm_struct, because it
* is dynamically sized based on nr_cpu_ids.
Quando você executa o build da ABI com esse patch aplicado, as ferramentas são encerradas com um código de erro diferente de zero e informam uma diferença de ABI semelhante a esta:
function symbol 'struct block_device* I_BDEV(struct inode*)' changed
CRC changed from 0x8d400dbd to 0xabfc92ad
function symbol 'void* PDE_DATA(const struct inode*)' changed
CRC changed from 0xc3c38b5c to 0x7ad96c0d
function symbol 'void __ClearPageMovable(struct page*)' changed
CRC changed from 0xf489e5e8 to 0x92bd005e
... 4492 omitted; 4495 symbols have only CRC changes
type 'struct mm_struct' changed
byte size changed from 992 to 1000
member 'int tickle_count' was added
member 'unsigned long cpu_bitmap[0]' changed
offset changed by 64
Diferenças de ABI detectadas no momento da build
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, primeiro verifique
se ele foi exportado com
EXPORT_SYMBOL_GPL(symbol_name)
e atualize a
lista de símbolos e a representação da ABI. Por exemplo, as mudanças a seguir adicionam o
novo recurso de sistema de arquivos incremental à ramificação android-12-5.10
, que inclui
a atualização da lista de símbolos e da representação da ABI.
- O exemplo de mudança de recurso está em aosp/1345659.
- O exemplo de lista de símbolos está em aosp/1346742.
- Um exemplo de mudança na representação da ABI está em aosp/1349377.
Se o símbolo for exportado (por você ou anteriormente), mas nenhum outro driver estiver usando, você poderá receber um erro de build 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 isso, atualize a lista de símbolos KMI no kernel e no ACK. Consulte Atualizar a representação da ABI. Para um exemplo de atualização de uma lista de símbolos e da representação de ABI no ACK, consulte aosp/1367601.
Resolver falhas de ABI do kernel
É possível lidar com quebras de ABI do kernel refatorando o código para não mudar 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 falhas de ABI
Refatorar o código para evitar mudanças na ABI
Faça o possível para evitar modificar a ABI atual. Em muitos casos, é possível refatorar o código para remover mudanças que afetam a ABI.
Refatoração de mudanças no campo da estrutura. Se uma mudança modificar a ABI de um recurso de depuração, adicione um
#ifdef
ao redor dos campos (nas structs e referências de origem) e verifique se oCONFIG
usado para o#ifdef
está desativado para a defconfig de produção egki_defconfig
. Para um exemplo de como uma configuração de depuração pode ser adicionada a uma struct sem interromper a ABI, consulte este conjunto de patches.Refatoração de recursos para não mudar o kernel principal. Se for necessário adicionar novos recursos ao ACK para oferecer suporte aos módulos do parceiro, tente refatorar a parte da ABI da mudança para evitar modificar a ABI do kernel. Para um exemplo de uso da ABI do kernel atual para adicionar outros recursos sem mudar a ABI do kernel, consulte aosp/1312213.
Corrigir uma ABI corrompida no Android Gerrit
Se você não quebrou a ABI do kernel intencionalmente, investigue usando as orientações fornecidas pelas ferramentas de monitoramento de ABI. As causas mais comuns de falhas são estruturas de dados alteradas e as mudanças associadas no CRC do símbolo ou mudanças nas opções de configuração que levam a qualquer uma das situações mencionadas. Comece corrigindo os problemas encontrados pela ferramenta.
É possível reproduzir os resultados da ABI localmente. Consulte Criar o kernel e a representação da ABI.
Sobre os rótulos Lint-1
Se você fizer upload de mudanças em uma ramificação que contenha uma KMI congelada ou finalizada, as mudanças precisarão passar por análises de conformidade e compatibilidade de ABI para garantir que as mudanças na representação de ABI reflitam a ABI real e não contenham incompatibilidades (remoções de símbolos ou mudanças de tipo).
Cada uma dessas análises de ABI pode definir o rótulo Lint-1 e bloquear o envio de mudanças até que todos os problemas sejam resolvidos ou o rótulo seja substituído.
Atualizar a ABI do kernel
Se for inevitável modificar a ABI, aplique as mudanças de código, a representação da ABI e a lista de símbolos ao ACK. Para que o Lint remova o -1 e não quebre a compatibilidade com o GKI, siga estas etapas:
Aguarde receber uma revisão de código +2 para o conjunto de patches.
Mescle as mudanças no código e na atualização da ABI.
Fazer upload das mudanças no código da ABI para o ACK
A atualização da ABI do ACK depende do tipo de mudança que está sendo feita.
Se uma mudança de ABI estiver relacionada a um recurso que afeta os testes do CTS ou do VTS, a mudança geralmente poderá ser escolhida 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 mudança de ABI for para um recurso que pode ser compartilhado com o ACK, essa mudança poderá ser escolhida para o ACK como está. Por exemplo, as seguintes mudanças não são necessárias para o teste do CTS ou do VTS, mas podem ser compartilhadas com o ACK:
- aosp/1250412 é uma mudança no recurso térmico.
- aosp/1288857
é uma mudança de
EXPORT_SYMBOL_GPL
.
Se uma mudança 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.
Usar stubs para ACK
Os stubs só podem ser necessários para mudanças no kernel principal que não beneficiam o ACK, como mudanças de desempenho e energia. A lista a seguir detalha exemplos de stubs e cherry-picks parciais no ACK para GKI.
Stub de recurso de isolamento de núcleo (aosp/1284493). As funcionalidades no ACK não são necessárias, mas os símbolos precisam estar presentes no ACK para que seus módulos usem esses símbolos.
Símbolo de marcador de posição para módulo do fornecedor. (aosp/1288860)
Cherry-pick somente de ABI do recurso de acompanhamento de eventos
mm
por processo (aosp/1288454). O patch original foi escolhido para ACK e depois cortado para incluir apenas as mudanças necessárias para resolver a diferença de ABI paratask_struct
emm_event_count
. Esse patch também atualiza a enumeraçãomm_event_type
para conter os membros finais.Cherry-pick parcial das mudanças na ABI da estrutura térmica que exigiram mais do que apenas adicionar os novos campos da ABI.
O patch aosp/1255544 resolveu as diferenças de ABI entre o kernel do parceiro e o ACK.
O patch aosp/1291018 corrigiu os problemas funcionais encontrados durante o teste do GKI do patch anterior. A correção incluiu a inicialização da struct de parâmetro do sensor para registrar várias zonas térmicas em um único sensor.
Mudanças na ABI
CONFIG_NL80211_TESTMODE
(aosp/1344321). O patch adicionou as mudanças necessárias na struct para ABI e garantiu que os campos extras não causassem diferenças funcionais, permitindo que os parceiros incluíssemCONFIG_NL80211_TESTMODE
nos kernels de produção e ainda mantivessem a conformidade com a GKI.
Aplicar a KMI no ambiente 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 os 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 exige um
símbolo não exportado é negado. Essa restrição é aplicada no momento da build, e as entradas ausentes são sinalizadas.
Para fins de desenvolvimento, você pode usar um build de kernel GKI que não inclua
corte de símbolos. Isso significa que todos os símbolos geralmente exportados podem ser usados. Para localizar
esses builds, procure os builds kernel_debug_aarch64
em
ci.android.com.
Aplicar o KMI usando o controle de versões do módulo
Os kernels de imagem genérica do kernel (GKI) usam o controle de versões do módulo
(CONFIG_MODVERSIONS
) como uma medida adicional para aplicar a conformidade do KMI em
tempo de execução. O controle de versões do módulo pode causar falhas de incompatibilidade de verificação de redundância cíclica (CRC) no momento do carregamento do módulo se o KMI esperado de um módulo não corresponder ao KMI do vmlinux
. Por exemplo, esta é 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 do controle de versões do módulo
O controle de versões de módulos é útil pelos seguintes motivos:
O controle de versões do módulo detecta mudanças na visibilidade da estrutura de dados. Se os módulos mudarem estruturas de dados opacas, ou seja, estruturas que não fazem parte da KMI, eles vão falhar após mudanças futuras na estrutura.
Por exemplo, considere o campo
fwnode
emstruct device
. Esse campo PRECISA ser opaco para que os módulos não possam fazer mudanças nos campos dedevice->fw_node
nem fazer suposições sobre o tamanho dele.No entanto, se um módulo incluir
<linux/fwnode.h>
(direta ou indiretamente), o campofwnode
nostruct device
não será mais opaco para ele. Em seguida, o módulo pode fazer mudanças emdevice->fwnode->dev
oudevice->fwnode->ops
. Esse cenário é problemático por vários motivos, como:Ele pode violar as proposições que o código do kernel principal faz sobre as estruturas de dados internas.
Se uma atualização futura do kernel mudar o
struct fwnode_handle
(o tipo de dados defwnode
), o módulo não vai mais funcionar com o novo kernel. Além disso,stgdiff
não vai mostrar nenhuma diferença porque o módulo está violando a KMI ao manipular diretamente 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 a KMI quando é carregado em uma data posterior por um novo kernel incompatível. O controle de versões do módulo adiciona uma verificação de tempo de execução para evitar o carregamento acidental de um módulo que não é compatível com a KMI do kernel. Essa verificação evita problemas de tempo de execução difíceis de depurar e falhas do kernel que podem resultar de uma incompatibilidade não detectada na KMI.
Ativar o controle de versões do módulo evita todos esses problemas.
Verificar se há incompatibilidades de CRC sem inicializar o dispositivo
stgdiff
compara e informa incompatibilidades de CRC entre kernels, além de outras diferenças de ABI.
Além disso, um build completo do kernel com CONFIG_MODVERSIONS
ativado gera um
arquivo Module.symvers
como parte do processo de build normal. Esse arquivo tem uma
linha para cada símbolo exportado pelo kernel (vmlinux
) e pelos módulos. Cada
linha consiste no valor de CRC, no nome do símbolo, no namespace do símbolo, no vmlinux
ou
nome do módulo que está exportando o símbolo e no tipo de exportação (por exemplo,
EXPORT_SYMBOL
versus EXPORT_SYMBOL_GPL
).
Compare os arquivos Module.symvers
entre o build do GKI e o seu para verificar se há diferenças de CRC nos símbolos exportados por vmlinux
. Se houver uma diferença no valor de CRC em qualquer símbolo exportado por vmlinux
e e esse símbolo for usado por um dos módulos carregados no dispositivo, o módulo não será carregado.
Se você não tiver todos os artefatos de build, mas tiver os arquivos vmlinux
do kernel GKI e do seu kernel, compare os valores de CRC de um símbolo específico executando o seguinte comando nos dois kernels e comparando a saída:
nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>
Por exemplo, o comando a seguir verifica o valor de CRC do símbolo module_layout
:
nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout
Resolver incompatibilidades de CRC
Siga estas etapas para resolver uma incompatibilidade de CRC ao carregar um módulo:
Crie o kernel da GKI e o kernel do dispositivo usando a opção
--kbuild_symtypes
conforme mostrado no comando a seguir:tools/bazel run --kbuild_symtypes //common:kernel_aarch64_dist
Esse comando gera um arquivo
.symtypes
para cada arquivo.o
. ConsulteKBUILD_SYMTYPES
no Kleaf para mais detalhes.Para o Android 13 e versões anteriores, crie o kernel da GKI e o kernel do dispositivo adicionando
KBUILD_SYMTYPES=1
ao comando que você usa para criar o kernel, conforme mostrado no comando a seguir:KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
Ao usar
build_abi.sh,
, a flagKBUILD_SYMTYPES=1
já é definida implicitamente.Encontre o arquivo
.c
em que o símbolo com incompatibilidade de CRC é exportado usando o seguinte comando:git -C common grep EXPORT_SYMBOL.*module_layout kernel/module/version.c:EXPORT_SYMBOL(module_layout);
O arquivo
.c
tem um arquivo.symtypes
correspondente no GKI e nos artefatos de build do kernel do dispositivo. Localize o arquivo.symtypes
usando os seguintes comandos:cd bazel-bin/common/kernel_aarch64/symtypes ls -1 kernel/module/version.symtypes
No Android 13 e versões anteriores, usando os scripts de build legados, o local provavelmente será
out/$BRANCH/common
ouout_abi/$BRANCH/common
.Cada arquivo
.symtypes
é um arquivo de texto simples que consiste em descrições de tipo e símbolo:Cada linha tem o formato
key description
, em que a descrição pode se referir a outras chaves no mesmo arquivo.Chaves como
[s|u|e|t]#foo
se referem a[struct|union|enum|typedef] foo
. Exemplo:t#bool typedef _Bool bool
As chaves sem prefixo
x#
são apenas nomes de símbolos. Exemplo:find_module s#module * find_module ( const char * )
Compare os dois arquivos e corrija todas as diferenças.
É melhor gerar symtypes
com um build logo antes da mudança
problemática e depois na mudança problemática. Salvar todos os arquivos significa que eles podem ser comparados em massa.
Por exemplo:
for f in $(find good bad -name '*.symtypes' | sed -r 's;^(good|bad)/;;' | LANG=C sort -u); do
diff -N -U0 --label good/"$f" --label bad/"$f" <(LANG=C sort good/"$f") <(LANG=C sort bad/"$f")
done
Caso contrário, compare apenas os arquivos específicos de interesse.
Caso 1: diferenças devido à visibilidade do tipo de dado
Um novo #include
pode extrair uma nova definição de tipo (por exemplo, de struct foo
) para um arquivo de origem. Nesses casos, a descrição no arquivo .symtypes
correspondente muda de um structure_type foo { }
vazio para uma definição completa.
Isso vai afetar todos os CRCs de todos os símbolos no arquivo .symtypes
cujas
descrições dependem direta ou indiretamente da definição de tipo.
Por exemplo, adicionar a seguinte linha ao arquivo
include/linux/device.h
no kernel causa incompatibilidades de CRC, uma das quais
é para module_layout()
:
#include <linux/fwnode.h>
Comparar o module/version.symtypes
desse símbolo expõe as seguintes diferenças:
$ diff -u <GKI>/kernel/module/version.symtypes <your kernel>/kernel/module/version.symtypes
--- <GKI>/kernel/module/version.symtypes
+++ <your kernel>/kernel/module/version.symtypes
@@ -334,12 +334,15 @@
...
-s#fwnode_handle structure_type fwnode_handle { }
+s#fwnode_reference_args structure_type fwnode_reference_args { s#fwnode_handle * fwnode ; unsigned int nargs ; t#u64 args [ 8 ] ; }
...
Se o kernel do GKI tiver a definição de tipo completa, mas o seu não tiver (muito improvável), mescle o kernel comum do Android mais recente no seu para usar a base do kernel do GKI mais recente.
Na maioria dos casos, o kernel GKI não tem a definição de tipo completa em
.symtypes
, mas seu kernel tem devido a outras diretivas #include
.
Resolução para Android 16 e versões mais recentes
Verifique se o arquivo de origem afetado inclui o cabeçalho de estabilização da KABI do Android:
#include <linux/android_kabi.h>
Para cada tipo afetado, adicione ANDROID_KABI_DECLONLY(name);
no escopo global ao arquivo de origem afetado.
Por exemplo, se a diferença de symtypes
for esta:
--- good/drivers/android/vendor_hooks.symtypes
+++ bad/drivers/android/vendor_hooks.symtypes
@@ -1051 +1051,2 @@
-s#ubuf_info structure_type ubuf_info { }
+s#ubuf_info structure_type ubuf_info { member pointer_type { const_type { s#ubuf_info_ops } } ops data_member_location(0) , member t#refcount_t refcnt data_member_location(8) , member t#u8 flags data_member_location(12) } byte_size(16)
+s#ubuf_info_ops structure_type ubuf_info_ops { member pointer_type { subroutine_type ( formal_parameter pointer_type { s#sk_buff } , formal_parameter pointer_type { s#ubuf_info } , formal_parameter t#bool ) -> base_type void } complete data_member_location(0) , member pointer_type { subroutine_type ( formal_parameter pointer_type { s#sk_buff } , formal_parameter pointer_type { s#ubuf_info } ) -> base_type int byte_size(4) encoding(5) } link_skb data_member_location(8) } byte_size(16)
O problema é que struct ubuf_info
agora tem uma definição completa em
symtypes
. A solução é adicionar uma linha a drivers/android/vendor_hooks.c
:
ANDROID_KABI_DECLONLY(ubuf_info);
Isso instrui o gendwarfksyms
a tratar o tipo nomeado como indefinido no arquivo.
Uma possibilidade mais complexa é que o novo #include
esteja em um arquivo de cabeçalho. Nesse caso, talvez seja necessário distribuir diferentes conjuntos de
invocações de macro ANDROID_KABI_DECLONLY
em arquivos de origem que indiretamente
extraem definições de tipo extras, já que alguns deles podem já ter algumas das
definições de tipo.
Para facilitar a leitura, coloque essas invocações de macro perto do início do arquivo de origem.
Resolução para Android 15 e versões anteriores
Muitas vezes, a correção é apenas ocultar o novo #include
de genksyms
.
#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif
Caso contrário, para identificar o #include
que causa a diferença, siga estas etapas:
Abra o arquivo de cabeçalho que define o símbolo ou o 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 de cima 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 o módulo. O erro de tempo de build resultante mostra a cadeia de arquivo de cabeçalho
#include
que levou a essa incompatibilidade de CRC. 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 links nessa cadeia de
#include
é devido a uma mudança feita no seu kernel, que está faltando no kernel GKI.
Caso 2: diferenças devido a mudanças no tipo de dados
Se a incompatibilidade de CRC de um símbolo ou tipo de dados não for devido a uma diferença de visibilidade, ela será causada por mudanças reais (adições, remoções ou alterações) no próprio tipo de dados.
Por exemplo, fazer a seguinte mudança no kernel causa várias incompatibilidades de CRC, já que muitos símbolos são afetados indiretamente por esse tipo de mudança:
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
desse símbolo, eles podem 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 structure_type 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 structure_type 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 entre seu kernel e o kernel GKI, encontre o commit executando o seguinte comando:
git blame
- Para símbolos excluídos (quando um símbolo é excluído em uma árvore e você também quer excluí-lo na outra), encontre a mudança 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 de commits retornada para localizar a mudança ou exclusão. O primeiro commit é provavelmente o que você está procurando. Caso contrário, percorra a lista até encontrar o commit.
Depois de identificar o commit, reverta-o no kernel ou atualize-o para suprimir a mudança de CRC e fazer upload para o ACK e mesclar. Cada quebra de ABI residual precisa ser analisada quanto à segurança e, se necessário, uma quebra permitida pode ser registrada.
Preferir consumir o padding atual
Algumas estruturas no GKI são preenchidas para permitir a extensão sem interromper os módulos do fornecedor atuais. Se um commit upstream (por exemplo) adicionar um membro a uma estrutura desse tipo, talvez seja possível mudar para consumir parte do padding. Essa mudança é ocultada do cálculo de CRC.
A macro padronizada e autodocumentada ANDROID_KABI_RESERVE
reserva um espaço de u64
(alinhado). Ele é usado no lugar de uma declaração de membro.
Exemplo:
struct data {
u64 handle;
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
};
O padding pode ser consumido, sem afetar os CRCs de símbolos, com ANDROID_KABI_USE
(ou ANDROID_KABI_USE2
ou outras variantes que podem ser definidas).
O membro sekret
está disponível como se tivesse sido declarado diretamente, mas a macro
é expandida para um membro de união anônimo que contém sekret
e
itens usados por gendwarfksyms
para manter a estabilidade do symtype.
struct data {
u64 handle;
ANDROID_KABI_USE(1, void *sekret);
ANDROID_KABI_RESERVE(2);
};
Resolução para Android 16 e versões mais recentes
Os CRCs são calculados por gendwarfksyms
, que usa informações de depuração DWARF, oferecendo suporte aos tipos C e Rust. A resolução varia de acordo com o tipo de mudança. Veja alguns exemplos.
Enumeradores novos ou modificados
Às vezes, novos enumeradores são adicionados, e ocasionalmente um valor de enumerador MAX
ou semelhante também é afetado. Essas mudanças são seguras se não "escaparem" do
GKI ou se pudermos ter certeza de que os módulos do fornecedor não se importam com os valores deles.
Exemplo:
enum outcome {
SUCCESS,
FAILURE,
RETRY,
+ TRY_HARDER,
OUTCOME_LIMIT
};
A adição de TRY_HARDER
e a mudança para OUTCOME_LIMIT
podem ser ocultadas do
cálculo de CRC com invocações de macro no escopo global:
ANDROID_KABI_ENUMERATOR_IGNORE(outcome, TRY_HARDER);
ANDROID_KABI_ENUMERATOR_VALUE(outcome, OUTCOME_LIMIT, 3);
Para facilitar a leitura, coloque-as logo após a definição de enum
.
Um novo membro da estrutura ocupando um buraco existente
Devido ao alinhamento, haverá bytes não utilizados entre urgent
e scratch
.
void *data;
bool urgent;
+ bool retry;
void *scratch;
Nenhum deslocamento de membro ou tamanho da estrutura é afetado pela adição de retry
. No entanto, isso pode afetar os CRCs de símbolos, a representação da ABI ou ambos.
Isso vai ocultar o valor do cálculo de CRC:
void *data;
bool urgent;
+ ANDROID_KABI_IGNORE(1, bool retry);
void *scratch_space;
O membro retry
está disponível como se tivesse sido declarado diretamente, mas a macro
é expandida para um membro de união anônimo que contém retry
e
itens usados por gendwarfksyms
para manter a estabilidade do symtype.
Extensão de uma estrutura com novos membros
Às vezes, os membros são adicionados ao final de uma estrutura. Isso não afeta os deslocamentos de membros atuais nem os usuários atuais da estrutura que só a acessam por ponteiro. O tamanho da estrutura afeta o CRC, e as mudanças podem ser suprimidas com uma invocação de macro extra no escopo global, da seguinte forma:
struct data {
u64 handle;
u64 counter;
ANDROID_KABI_IGNORE(1, void *sekret);
};
ANDROID_KABI_BYTE_SIZE(data, 16);
Para facilitar a leitura, coloque isso logo após a definição de struct
.
Todas as outras mudanças em um tipo ou no tipo de um símbolo
Em raras ocasiões, pode haver mudanças que não se enquadram em uma das categorias anteriores, resultando em mudanças de CRC que não podem ser suprimidas usando as macros anteriores.
Nesses casos, a descrição original symtypes
de um tipo ou símbolo pode ser
fornecida com uma invocação de ANDROID_KABI_TYPE_STRING
no escopo global.
struct data {
/* extensive changes */
};
ANDROID_KABI_TYPE_STRING("s#data", "original s#data symtypes definition");
Para facilitar a leitura, coloque isso logo após a definição do tipo ou do símbolo.
Resolução para Android 15 e versões anteriores
As mudanças de tipo e tipo de símbolo precisam ser ocultadas de genksyms
. Isso pode ser feito
controlando o pré-processamento com __GENKSYMS__
.
Transformações de código arbitrárias podem ser expressas dessa forma.
Por exemplo, para ocultar um novo membro que ocupa um espaço em uma estrutura existente:
struct parcel {
void *data;
bool urgent;
#ifndef __GENKSYMS__
bool retry;
#endif
void *scratch_space;
};