Vous pouvez utiliser les outils de surveillance de l'interface binaire d'application (ABI), disponibles dans Android 11 et versions ultérieures, pour stabiliser l'ABI du noyau dans les noyaux Android. L'outil collecte et compare les représentations ABI à partir des binaires du noyau existants (modules vmlinux
+ GKI). Ces représentations ABI sont les fichiers .stg
et les listes de symboles. L'interface sur laquelle la représentation donne une vue est appelée interface du module du noyau (KMI, Kernel Module Interface). Vous pouvez utiliser l'outil pour suivre et atténuer les modifications apportées à l'IKM.
L'outil de surveillance de l'ABI est développé dans AOSP et utilise STG (ou libabigail
dans Android 13 et les versions antérieures) pour générer et comparer les représentations.
Cette page décrit l'outillage, le processus de collecte et d'analyse des représentations ABI, ainsi que l'utilisation de ces représentations pour assurer la stabilité de l'ABI dans le noyau. Cette page fournit également des informations pour contribuer aux modifications apportées aux noyaux Android.
Procédure
L'analyse de l'ABI du noyau nécessite plusieurs étapes, dont la plupart peuvent être automatisées :
- Compilez le noyau et sa représentation ABI.
- Analysez les différences d'ABI entre la build et une référence.
- Mettez à jour la représentation ABI (si nécessaire).
- Utiliser des listes de symboles
Les instructions suivantes fonctionnent pour tout noyau que vous pouvez compiler à l'aide d'une chaîne d'outils compatible (telle que la chaîne d'outils Clang précompilée). Les repo manifests
sont disponibles pour toutes les branches de noyau commun Android et pour plusieurs noyaux spécifiques à des appareils. Ils permettent de vérifier que la bonne chaîne d'outils est utilisée lorsque vous créez une distribution de noyau pour l'analyse.
Listes de symboles
Le KMI n'inclut pas tous les symboles du noyau,ni même tous les plus de 30 000 symboles exportés. Au lieu de cela, les symboles qui peuvent être utilisés par les modules du fournisseur sont explicitement listés dans un ensemble de fichiers de liste de symboles disponibles publiquement dans l'arborescence du noyau (gki/{ARCH}/symbols/*
ou android/abi_gki_{ARCH}_*
dans Android 15 et versions antérieures). L'union de tous les symboles de tous les fichiers de liste de symboles définit l'ensemble des symboles KMI maintenus comme stables. Un exemple de fichier de liste de symboles est gki/aarch64/symbols/db845c
, qui déclare les symboles requis pour la DragonBoard 845c.
Seuls les symboles listés dans une liste de symboles, ainsi que leurs structures et définitions associées, sont considérés comme faisant partie des informations clés sur le matériel. Vous pouvez publier des modifications apportées à vos listes de symboles si les symboles dont vous avez besoin ne sont pas présents. Une fois que les nouvelles interfaces figurent dans une liste de symboles et font partie de la description de l'IKM, elles sont considérées comme stables et ne doivent pas être supprimées de la liste de symboles ni modifiées une fois la branche figée.
Chaque branche de noyau KMI du noyau commun Android (ACK) possède son propre ensemble de listes de symboles. Aucune tentative n'est faite pour assurer la stabilité de l'ABI entre les différentes branches du noyau KMI. Par exemple, le KMI pour android12-5.10
est complètement indépendant de celui pour android13-5.10
.
Les outils ABI utilisent des listes de symboles KMI pour limiter les interfaces qui doivent être surveillées pour la stabilité. Les fournisseurs sont censés envoyer et mettre à jour leurs propres listes de symboles pour vérifier que les interfaces sur lesquelles ils s'appuient conservent la compatibilité ABI. Par exemple, pour afficher la liste des listes de symboles pour le noyau android16-6.12
, consultez https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols
.
Une liste de symboles contient les symboles qui sont nécessaires pour le fournisseur ou l'appareil concerné. La liste complète utilisée par les outils est l'union de tous les fichiers de liste de symboles KMI. Les outils ABI déterminent les détails de chaque symbole, y compris la signature de la fonction et les structures de données imbriquées.
Lorsque le KMI est figé, aucune modification n'est autorisée pour les interfaces KMI existantes. Elles sont stables. Toutefois, les fournisseurs sont libres d'ajouter des symboles au KMI à tout moment, à condition que ces ajouts n'affectent pas la stabilité de l'ABI existante. Les symboles nouvellement ajoutés sont considérés comme stables dès qu'ils sont cités par une liste de symboles KMI. Les symboles ne doivent pas être supprimés d'une liste pour un noyau, sauf s'il est confirmé qu'aucun appareil n'a jamais été livré avec une dépendance à ce symbole.
Vous pouvez générer une liste de symboles KMI pour un appareil en suivant les instructions de la section Utiliser des listes de symboles. De nombreux partenaires envoient une liste de symboles par ACK, mais ce n'est pas une exigence stricte. Si cela facilite la maintenance, vous pouvez envoyer plusieurs listes de symboles.
Élargir le KMI
Bien que les symboles KMI et les structures associées soient considérés comme stables (ce qui signifie que les modifications qui rompent les interfaces stables dans un noyau avec un KMI figé ne peuvent pas être acceptées), le noyau GKI reste ouvert aux extensions afin que les appareils expédiés plus tard dans l'année n'aient pas besoin de définir toutes leurs dépendances avant que le KMI ne soit figé. Pour étendre l'IKM, vous pouvez ajouter de nouveaux symboles à l'IKM pour les fonctions de noyau exportées nouvelles ou existantes, même si l'IKM est figé. De nouveaux correctifs du noyau peuvent également être acceptés s'ils ne rompent pas l'interface KMI.
À propos des ruptures de KMI
Un noyau comporte des sources à partir desquelles les binaires sont créés.
Les branches du noyau surveillées par l'ABI incluent une représentation de l'ABI GKI actuelle (sous la forme d'un fichier .stg
). Une fois les binaires (vmlinux
, Image
et tous les modules GKI) créés, une représentation ABI peut être extraite des binaires. Toute modification apportée à un fichier source du noyau peut affecter les binaires et, par conséquent, les .stg
extraits. L'analyse de la conformité de l'ABI compare le fichier .stg
validé à celui extrait des artefacts de compilation et définit un libellé Lint-1 sur la modification dans Gerrit s'il détecte une différence sémantique.
Gérer les ruptures d'ABI
Par exemple, le correctif suivant introduit une rupture d'ABI très évidente :
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.
Lorsque vous exécutez la compilation ABI avec ce correctif appliqué, l'outil se ferme avec un code d'erreur différent de zéro et signale une différence ABI semblable à celle-ci :
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
Différences d'ABI détectées au moment de la compilation
La cause la plus fréquente d'erreurs est l'utilisation par un pilote d'un nouveau symbole du noyau qui ne figure dans aucune des listes de symboles.
Si le symbole n'est pas inclus dans votre liste de symboles, vous devez d'abord vérifier qu'il est exporté avec EXPORT_SYMBOL_GPL(symbol_name)
, puis mettre à jour la liste de symboles et la représentation ABI. Par exemple, les modifications suivantes ajoutent la nouvelle fonctionnalité Incremental FS à la branche android-12-5.10
, qui inclut la mise à jour de la liste des symboles et de la représentation ABI.
- Un exemple de modification de fonctionnalité est disponible dans aosp/1345659.
- Un exemple de liste de symboles est disponible dans aosp/1346742.
- Un exemple de modification de la représentation de l'ABI est disponible dans aosp/1349377.
Si le symbole est exporté (par vous ou précédemment), mais qu'aucun autre pilote ne l'utilise, une erreur de compilation semblable à celle ci-dessous peut s'afficher.
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
Pour résoudre ce problème, mettez à jour la liste des symboles KMI dans votre noyau et dans l'ACK (consultez Mettre à jour la représentation de l'ABI). Pour obtenir un exemple de mise à jour d'une liste de symboles et de la représentation ABI dans l'ACK, consultez aosp/1367601.
Résoudre les problèmes de rupture de l'ABI du noyau
Vous pouvez gérer les ruptures d'ABI du noyau en refactorisant le code pour ne pas modifier l'ABI ou en mettant à jour la représentation de l'ABI. Utilisez le tableau suivant pour déterminer l'approche la mieux adaptée à votre situation.
Figure 1 : Résolution des problèmes de rupture d'ABI
Refactoriser le code pour éviter les modifications de l'ABI
Faites tout votre possible pour éviter de modifier l'ABI existante. Dans de nombreux cas, vous pouvez refactoriser votre code pour supprimer les modifications qui affectent l'ABI.
Refactorisation des modifications apportées aux champs de structure. Si une modification modifie l'ABI d'une fonctionnalité de débogage, ajoutez un
#ifdef
autour des champs (dans les structs et les références de source) et assurez-vous que leCONFIG
utilisé pour le#ifdef
est désactivé pour la configuration de définition de production etgki_defconfig
. Pour obtenir un exemple d'ajout d'une configuration de débogage à une structure sans casser l'ABI, consultez cet ensemble de correctifs.Refactorisation des fonctionnalités pour ne pas modifier le noyau principal. Si de nouvelles fonctionnalités doivent être ajoutées à ACK pour prendre en charge les modules partenaires, essayez de refactoriser la partie ABI de la modification pour éviter de modifier l'ABI du noyau. Pour obtenir un exemple d'utilisation de l'ABI du noyau existant afin d'ajouter des fonctionnalités sans modifier l'ABI du noyau, consultez aosp/1312213.
Corriger une ABI défectueuse sur Android Gerrit
Si vous n'avez pas intentionnellement enfreint l'ABI du noyau, vous devez examiner le problème à l'aide des conseils fournis par l'outil de surveillance de l'ABI. Les causes les plus courantes de ces problèmes sont les modifications apportées aux structures de données et aux codes CRC des symboles associés, ou les modifications des options de configuration qui entraînent l'un des problèmes susmentionnés. Commencez par résoudre les problèmes détectés par l'outil.
Vous pouvez reproduire les résultats de l'ABI en local. Pour en savoir plus, consultez Compiler le noyau et sa représentation ABI.
À propos des libellés Lint-1
Si vous importez des modifications dans une branche contenant une KMI figée ou finalisée, elles doivent passer les analyses de conformité et de compatibilité de l'ABI pour s'assurer que les modifications apportées à la représentation de l'ABI reflètent l'ABI réelle et ne contiennent aucune incompatibilité (suppression de symboles ou modifications de types).
Chacune de ces analyses ABI peut définir le libellé Lint-1 et bloquer l'envoi des modifications jusqu'à ce que tous les problèmes soient résolus ou que le libellé soit remplacé.
Mettre à jour l'ABI du noyau
Si vous ne pouvez pas éviter de modifier l'ABI, vous devez appliquer vos modifications de code, la représentation de l'ABI et la liste des symboles à l'ACK. Pour que Lint supprime le "-1" et ne rompe pas la compatibilité GKI, procédez comme suit :
Attendez de recevoir un Code-Review +2 pour l'ensemble de correctifs.
Fusionnez vos modifications de code et la modification de la mise à jour de l'ABI.
Importer les modifications du code ABI dans ACK
La mise à jour de l'ABI ACK dépend du type de modification apportée.
Si une modification de l'ABI est liée à une fonctionnalité qui affecte les tests CTS ou VTS, la modification peut généralement être sélectionnée pour être ACK telle quelle. Par exemple :
- aosp/1289677 est nécessaire pour que le son fonctionne.
- aosp/1295945 est nécessaire pour que l'USB fonctionne.
Si une modification de l'ABI concerne une fonctionnalité qui peut être partagée avec l'ACK, cette modification peut être sélectionnée pour l'ACK telle quelle. Par exemple, les modifications suivantes ne sont pas nécessaires pour les tests CTS ou VTS, mais peuvent être partagées avec l'ACK :
- aosp/1250412 est une modification de la fonctionnalité thermique.
- aosp/1288857 est une modification
EXPORT_SYMBOL_GPL
.
Si une modification de l'ABI introduit une nouvelle fonctionnalité qui n'a pas besoin d'être incluse dans l'ACK, vous pouvez introduire les symboles dans l'ACK à l'aide d'un stub, comme décrit dans la section suivante.
Utiliser des stubs pour ACK
Les stubs ne doivent être nécessaires que pour les modifications du noyau principal qui ne profitent pas à l'ACK, comme les modifications de performances et de puissance. La liste suivante fournit des exemples de stubs et de cherry-picks partiels dans ACK pour GKI.
Stub de fonctionnalité d'isolation du cœur (aosp/1284493). Les capacités d'ACK ne sont pas nécessaires, mais les symboles doivent être présents dans ACK pour que vos modules puissent les utiliser.
Symbole d'espace réservé pour le module du fournisseur (aosp/1288860).
Cherry-pick ABI uniquement de la fonctionnalité de suivi des événements
mm
par processus (aosp/1288454). Le correctif d'origine a été sélectionné pour ACK, puis réduit pour n'inclure que les modifications nécessaires à la résolution de la différence ABI pourtask_struct
etmm_event_count
. Ce correctif met également à jour l'énumérationmm_event_type
pour qu'elle contienne les membres finaux.Cherry-pick partiel des modifications de l'ABI de la structure thermique qui nécessitaient plus que l'ajout des nouveaux champs ABI.
Le correctif aosp/1255544 a résolu les différences d'ABI entre le noyau partenaire et ACK.
Le correctif aosp/1291018 a résolu les problèmes fonctionnels détectés lors des tests GKI du correctif précédent. La correction consistait à initialiser la structure du paramètre du capteur pour enregistrer plusieurs zones thermiques sur un seul capteur.
Modifications apportées à l'ABI
CONFIG_NL80211_TESTMODE
(aosp/1344321). Ce correctif a ajouté les modifications de struct nécessaires pour l'ABI et s'est assuré que les champs supplémentaires n'entraînaient pas de différences fonctionnelles, ce qui a permis aux partenaires d'inclureCONFIG_NL80211_TESTMODE
dans leurs noyaux de production tout en conservant la conformité GKI.
Appliquer le KMI au moment de l'exécution
Les noyaux GKI utilisent les options de configuration TRIM_UNUSED_KSYMS=y
et UNUSED_KSYMS_WHITELIST=<union
of all symbol lists>
, qui limitent les symboles exportés (tels que les symboles exportés à l'aide de EXPORT_SYMBOL_GPL()
) à ceux figurant sur une liste de symboles. Tous les autres symboles ne sont pas exportés, et le chargement d'un module nécessitant un symbole non exporté est refusé. Cette restriction est appliquée au moment de la compilation et les entrées manquantes sont signalées.
À des fins de développement, vous pouvez utiliser une compilation du noyau GKI qui n'inclut pas la suppression des symboles (ce qui signifie que tous les symboles habituellement exportés peuvent être utilisés). Pour localiser ces builds, recherchez les builds kernel_debug_aarch64
sur ci.android.com.
Appliquer l'IKM à l'aide du versionnage des modules
Les noyaux GKI (Generic Kernel Image) utilisent le versionnage des modules (CONFIG_MODVERSIONS
) comme mesure supplémentaire pour assurer la conformité KMI lors de l'exécution. La gestion des versions des modules peut entraîner des échecs de non-concordance de la vérification de redondance cyclique (CRC) lors du chargement des modules si le KMI attendu d'un module ne correspond pas au KMI vmlinux
. Par exemple, voici un échec typique qui se produit au moment du chargement du module en raison d'une incompatibilité CRC pour le symbole 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 ''
Utilisations de la gestion des versions des modules
La gestion des versions des modules est utile pour les raisons suivantes :
La gestion des versions des modules permet de détecter les modifications de la visibilité de la structure des données. Si les modules modifient des structures de données opaques (c'est-à-dire des structures de données qui ne font pas partie de l'IKM), ils ne fonctionneront plus après de futures modifications de la structure.
Prenons l'exemple du champ
fwnode
dansstruct device
. Ce champ DOIT être opaque pour les modules afin qu'ils ne puissent pas modifier les champs dedevice->fw_node
ni faire d'hypothèses sur sa taille.Toutefois, si un module inclut
<linux/fwnode.h>
(directement ou indirectement), le champfwnode
dansstruct device
ne lui est plus opaque. Le module peut ensuite apporter des modifications àdevice->fwnode->dev
oudevice->fwnode->ops
. Ce scénario pose problème pour plusieurs raisons :Cela peut enfreindre les hypothèses que le code du noyau principal fait sur ses structures de données internes.
Si une future mise à jour du noyau modifie
struct fwnode_handle
(le type de données defwnode
), le module ne fonctionnera plus avec le nouveau noyau. De plus,stgdiff
n'affichera aucune différence, car le module enfreint l'IKM en manipulant directement les structures de données internes d'une manière qui ne peut pas être capturée en inspectant uniquement la représentation binaire.
Un module actuel est considéré comme incompatible avec KMI lorsqu'il est chargé ultérieurement par un nouveau noyau incompatible. La gestion des versions des modules ajoute une vérification de l'exécution pour éviter de charger accidentellement un module qui n'est pas compatible avec le noyau KMI. Cette vérification permet d'éviter les problèmes d'exécution difficiles à déboguer et les plantages du noyau qui pourraient résulter d'une incompatibilité non détectée dans l'interface KMI.
L'activation de la gestion des versions des modules permet d'éviter tous ces problèmes.
Vérifier les incohérences de CRC sans démarrer l'appareil
stgdiff
compare les CRC entre les noyaux et signale les différences, ainsi que d'autres différences d'ABI.
De plus, une compilation complète du noyau avec CONFIG_MODVERSIONS
activé génère un fichier Module.symvers
dans le cadre du processus de compilation normal. Ce fichier comporte une ligne pour chaque symbole exporté par le noyau (vmlinux
) et les modules. Chaque ligne se compose de la valeur CRC, du nom du symbole, de l'espace de noms du symbole, du nom du module ou de vmlinux
qui exporte le symbole, et du type d'exportation (par exemple, EXPORT_SYMBOL
par rapport à EXPORT_SYMBOL_GPL
).
Vous pouvez comparer les fichiers Module.symvers
entre la compilation GKI et votre compilation pour vérifier s'il existe des différences de CRC dans les symboles exportés par vmlinux
. Si la valeur CRC d'un symbole exporté par vmlinux
and est différente et que ce symbole est utilisé par l'un des modules que vous chargez sur votre appareil, le module ne se charge pas.
Si vous ne disposez pas de tous les artefacts de compilation, mais que vous avez les fichiers vmlinux
du noyau GKI et de votre noyau, vous pouvez comparer les valeurs CRC d'un symbole spécifique en exécutant la commande suivante sur les deux noyaux et en comparant la sortie :
nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>
Par exemple, la commande suivante vérifie la valeur CRC du symbole module_layout
:
nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout
Résoudre les incohérences de CRC
Pour résoudre un problème de non-concordance du CRC lors du chargement d'un module, procédez comme suit :
Créez le noyau GKI et le noyau de votre appareil à l'aide de l'option
--kbuild_symtypes
, comme indiqué dans la commande suivante :tools/bazel run --kbuild_symtypes //common:kernel_aarch64_dist
Cette commande génère un fichier
.symtypes
pour chaque fichier.o
. Pour en savoir plus, consultezKBUILD_SYMTYPES
dans Kleaf.Pour Android 13 et les versions antérieures, créez le noyau GKI et le noyau de votre appareil en ajoutant
KBUILD_SYMTYPES=1
à la commande que vous utilisez pour créer le noyau, comme indiqué dans la commande suivante :KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
Lorsque vous utilisez
build_abi.sh,
, l'optionKBUILD_SYMTYPES=1
est déjà définie de manière implicite.Recherchez le fichier
.c
dans lequel le symbole avec un CRC non correspondant est exporté, à l'aide de la commande suivante :git -C common grep EXPORT_SYMBOL.*module_layout kernel/module/version.c:EXPORT_SYMBOL(module_layout);
Le fichier
.c
a un fichier.symtypes
correspondant dans le GKI et les artefacts de compilation du noyau de votre appareil. Localisez le fichier.symtypes
à l'aide des commandes suivantes :cd bazel-bin/common/kernel_aarch64/symtypes ls -1 kernel/module/version.symtypes
Dans Android 13 et versions antérieures, en utilisant les anciens scripts de compilation, l'emplacement est probablement
out/$BRANCH/common
ouout_abi/$BRANCH/common
.Chaque fichier
.symtypes
est un fichier en texte brut contenant des descriptions de type et de symbole :Chaque ligne est de la forme
key description
, où la description peut faire référence à d'autres clés du même fichier.Les clés telles que
[s|u|e|t]#foo
font référence à[struct|union|enum|typedef] foo
. Exemple :t#bool typedef _Bool bool
Les clés sans préfixe
x#
sont de simples noms de symboles. Exemple :find_module s#module * find_module ( const char * )
Comparez les deux fichiers et corrigez toutes les différences.
Il est préférable de générer symtypes
avec une version juste avant le changement problématique, puis au moment du changement problématique. En enregistrant tous les fichiers, vous pouvez les comparer de manière groupée.
Par exemple,
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
Sinon, comparez simplement les fichiers spécifiques qui vous intéressent.
Cas de figure 1 : Différences dues à la visibilité du type de données
Un nouveau #include
peut extraire une nouvelle définition de type (par exemple, struct foo
) dans un fichier source. Dans ce cas, sa description dans le fichier .symtypes
correspondant passe d'un structure_type foo { }
vide à une définition complète.
Cela affectera tous les CRC de tous les symboles du fichier .symtypes
dont les descriptions dépendent directement ou indirectement de la définition du type.
Par exemple, l'ajout de la ligne suivante au fichier include/linux/device.h
de votre noyau entraîne des incohérences de CRC, dont l'une concerne module_layout()
:
#include <linux/fwnode.h>
En comparant le module/version.symtypes
de ce symbole, les différences suivantes sont mises en évidence :
$ 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 ] ; }
...
Si le noyau GKI possède la définition de type complète, mais que votre noyau ne l'a pas (très peu probable), fusionnez le dernier noyau commun Android dans votre noyau afin d'utiliser la dernière base de noyau GKI.
Dans la plupart des cas, le noyau GKI ne contient pas la définition complète du type dans .symtypes
, mais votre noyau l'a en raison de directives #include
supplémentaires.
Résolution pour Android 16 et versions ultérieures
Assurez-vous que le fichier source concerné inclut l'en-tête de stabilisation Android KABI :
#include <linux/android_kabi.h>
Pour chaque type concerné, ajoutez ANDROID_KABI_DECLONLY(name);
à la portée globale du fichier source concerné.
Par exemple, si la différence symtypes
était la suivante :
--- 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)
Le problème est que struct ubuf_info
dispose désormais d'une définition complète dans symtypes
. La solution consiste à ajouter une ligne à drivers/android/vendor_hooks.c
:
ANDROID_KABI_DECLONLY(ubuf_info);
Cela indique à gendwarfksyms
de traiter le type nommé comme non défini dans le fichier.
Une autre possibilité plus complexe est que le nouveau #include
se trouve lui-même dans un fichier d'en-tête. Dans ce cas, vous devrez peut-être distribuer différents ensembles d'invocations de macros ANDROID_KABI_DECLONLY
dans les fichiers sources qui extraient indirectement des définitions de types supplémentaires, car certains d'entre eux peuvent déjà avoir certaines définitions de types.
Pour faciliter la lecture, placez ces appels de macro au début du fichier source.
Résolution pour Android 15 et versions antérieures
Souvent, la solution consiste simplement à masquer le nouveau #include
à partir de genksyms
.
#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif
Sinon, pour identifier le #include
qui est à l'origine de la différence, procédez comme suit :
Ouvrez le fichier d'en-tête qui définit le symbole ou le type de données présentant cette différence. Par exemple, modifiez
include/linux/fwnode.h
pourstruct fwnode_handle
.Ajoutez le code suivant en haut du fichier d'en-tête :
#ifdef CRC_CATCH #error "Included from here" #endif
Dans le fichier
.c
du module qui présente une erreur de CRC, ajoutez la ligne suivante comme première ligne avant toute ligne#include
.#define CRC_CATCH 1
Compilez votre module. L'erreur de compilation qui en résulte affiche la chaîne de fichiers d'en-tête
#include
qui a entraîné cette incohérence de CRC. Exemple :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"
L'un des liens de cette chaîne
#include
est dû à une modification apportée à votre noyau, qui est manquante dans le noyau GKI.
Cas de figure 2 : Différences dues à des modifications du type de données
Si le CRC ne correspond pas pour un symbole ou un type de données, et que cela n'est pas dû à une différence de visibilité, cela signifie que le type de données lui-même a été modifié (ajout, suppression ou modification).
Par exemple, si vous apportez la modification suivante à votre noyau, plusieurs erreurs de CRC se produiront, car de nombreux symboles sont indirectement affectés par ce type de modification :
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);
Un problème de correspondance du CRC concerne devm_of_platform_populate()
.
Si vous comparez les fichiers .symtypes
de ce symbole, ils peuvent ressembler à ceci :
$ 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 * ) ; ...
Pour identifier le type modifié, procédez comme suit :
Recherchez la définition du symbole dans le code source (généralement dans les fichiers
.h
).- Pour connaître les différences de symboles entre votre noyau et le noyau GKI, recherchez le commit en exécutant la commande suivante :
git blame
- Pour les symboles supprimés (lorsqu'un symbole est supprimé dans un arbre et que vous souhaitez également le supprimer dans l'autre arbre), vous devez trouver la modification qui a supprimé la ligne. Exécutez la commande suivante dans l'arborescence où la ligne a été supprimée :
git log -S "copy paste of deleted line/word" -- <file where it was deleted>
Passez en revue la liste des commits renvoyés pour localiser la modification ou la suppression. Le premier commit est probablement celui que vous recherchez. Si ce n'est pas le cas, parcourez la liste jusqu'à ce que vous trouviez le commit.
Une fois le commit identifié, restaurez-le dans votre noyau ou mettez-le à jour pour supprimer la modification du CRC et l'importer dans ACK afin qu'il soit fusionné. Chaque rupture d'ABI résiduelle devra être examinée pour vérifier sa sécurité et, si nécessaire, une rupture autorisée pourra être enregistrée.
Préférer consommer la marge intérieure existante
Certaines structures du noyau GKI sont complétées pour permettre leur extension sans casser les modules du fournisseur existants. Si un commit en amont (par exemple) ajoute un membre à une telle structure, il peut être possible de le modifier pour qu'il consomme une partie de la marge intérieure à la place. Cette modification est ensuite masquée du calcul du CRC.
La macro ANDROID_KABI_RESERVE
standardisée et auto-documentée réserve un espace de u64
(aligné). Il est utilisé à la place d'une déclaration de membre.
Exemple :
struct data {
u64 handle;
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
};
Le remplissage peut être consommé, sans affecter les CRC des symboles, avec ANDROID_KABI_USE
(ou ANDROID_KABI_USE2
ou d'autres variantes qui peuvent être définies).
Le membre sekret
est disponible comme s'il était déclaré directement, mais la macro se développe en fait en un membre d'union anonyme contenant sekret
ainsi que des éléments utilisés par gendwarfksyms
pour maintenir la stabilité du type de symbole.
struct data {
u64 handle;
ANDROID_KABI_USE(1, void *sekret);
ANDROID_KABI_RESERVE(2);
};
Résolution pour Android 16 et versions ultérieures
Les CRC sont calculés par gendwarfksyms
, qui utilise les informations de débogage DWARF et est donc compatible avec les types C et Rust. La résolution varie en fonction du type de modification. Voici quelques exemples :
Énumérateurs nouveaux ou modifiés
De nouveaux énumérateurs sont parfois ajoutés, et il arrive qu'une valeur d'énumérateur MAX
ou similaire soit également affectée. Ces modifications sont sûres si elles n'échappent pas au GKI ou si nous pouvons être sûrs que les modules du fournisseur ne se soucient pas de leurs valeurs.
Exemple :
enum outcome {
SUCCESS,
FAILURE,
RETRY,
+ TRY_HARDER,
OUTCOME_LIMIT
};
L'ajout de TRY_HARDER
et la modification de OUTCOME_LIMIT
peuvent être masqués du calcul du CRC avec des appels de macro au niveau global :
ANDROID_KABI_ENUMERATOR_IGNORE(outcome, TRY_HARDER);
ANDROID_KABI_ENUMERATOR_VALUE(outcome, OUTCOME_LIMIT, 3);
Pour améliorer la lisibilité, placez-les juste après la définition enum
.
Un nouveau membre de la structure occupant un trou existant
En raison de l'alignement, des octets inutilisés seront présents entre urgent
et scratch
.
void *data;
bool urgent;
+ bool retry;
void *scratch;
L'ajout de retry
n'a aucune incidence sur le décalage des membres existants ni sur la taille de la structure. Toutefois, cela peut affecter les CRC des symboles, la représentation ABI ou les deux.
Cela le masquera du calcul du CRC :
void *data;
bool urgent;
+ ANDROID_KABI_IGNORE(1, bool retry);
void *scratch_space;
Le membre retry
est disponible comme s'il était déclaré directement, mais la macro se développe en fait en un membre d'union anonyme contenant retry
ainsi que des éléments utilisés par gendwarfksyms
pour maintenir la stabilité du type de symbole.
Extension d'une structure avec de nouveaux membres
Les membres sont parfois ajoutés à la fin d'une structure. Cela n'a aucune incidence sur les décalages des membres existants ni sur les utilisateurs existants de la structure qui n'y accèdent que par pointeur. La taille de la structure affecte son CRC. Les modifications apportées à celui-ci peuvent être supprimées avec une invocation de macro supplémentaire à portée globale, comme suit :
struct data {
u64 handle;
u64 counter;
ANDROID_KABI_IGNORE(1, void *sekret);
};
ANDROID_KABI_BYTE_SIZE(data, 16);
Pour améliorer la lisibilité, placez-le juste après la définition struct
.
Toutes les autres modifications apportées à un type ou au type d'un symbole
Il peut arriver que des modifications ne correspondent à aucune des catégories précédentes, ce qui entraîne des modifications du CRC qui ne peuvent pas être supprimées à l'aide des macros précédentes.
Dans ce cas, la description symtypes
d'origine d'un type ou d'un symbole peut être fournie avec une invocation de ANDROID_KABI_TYPE_STRING
à portée globale.
struct data {
/* extensive changes */
};
ANDROID_KABI_TYPE_STRING("s#data", "original s#data symtypes definition");
Pour plus de lisibilité, placez-le juste après la définition du type ou du symbole.
Résolution pour Android 15 et versions antérieures
Les modifications du type et du type de symbole doivent être masquées dans genksyms
. Pour ce faire, contrôlez le prétraitement avec __GENKSYMS__
.
Les transformations de code arbitraires peuvent être exprimées de cette manière.
Par exemple, pour masquer un nouveau membre occupant un trou dans une structure existante :
struct parcel {
void *data;
bool urgent;
#ifndef __GENKSYMS__
bool retry;
#endif
void *scratch_space;
};