Mit den in Android 11 und höher verfügbaren Tools zur Überwachung der Application Binary Interface (ABI) können Sie die In-Kernel-ABI von Android-Kerneln stabilisieren. Das Tooling erfasst und vergleicht ABI-Darstellungen aus vorhandenen Kernel-Binärdateien (vmlinux
+ GKI-Module). Diese ABI-Darstellungen sind die .stg
-Dateien und die Symbollisten. Die Schnittstelle, über die die Darstellung eine Ansicht bietet, wird als Kernelmodulschnittstelle (Kernel Module Interface, KMI) bezeichnet. Mit den Tools können Sie Änderungen am KMI nachvollziehen und gegensteuern.
Die ABI-Monitoring-Tools werden in AOSP entwickelt und verwenden STG (oder libabigail
in Android 13 und niedriger), um Darstellungen zu generieren und zu vergleichen.
Auf dieser Seite werden die Tools, der Prozess zum Erfassen und Analysieren von ABI-Darstellungen sowie die Verwendung solcher Darstellungen zur Gewährleistung der Stabilität des In-Kernel-ABI beschrieben. Außerdem finden Sie hier Informationen dazu, wie Sie Änderungen an den Android-Kerneln beitragen können.
Prozess
Die Analyse des ABI des Kernels umfasst mehrere Schritte, von denen die meisten automatisiert werden können:
- Kernel und zugehörige ABI-Darstellung erstellen
- ABI-Unterschiede zwischen dem Build und einer Referenz analysieren
- ABI-Darstellung aktualisieren (falls erforderlich)
- Mit Symbollisten arbeiten
Die folgende Anleitung funktioniert für jeden Kernel, den Sie mit einer unterstützten Toolchain erstellen können, z. B. mit der vorgefertigten Clang-Toolchain. repo manifests
sind für alle gängigen Android-Kernel-Branches und für mehrere gerätespezifische Kernel verfügbar. Sie prüfen, ob die richtige Toolchain verwendet wird, wenn Sie eine Kernel-Distribution für die Analyse erstellen.
Symbollisten
Der KMI enthält nicht alle Symbole im Kernel und auch nicht alle der über 30.000 exportierten Symbole. Stattdessen werden die Symbole, die von Anbietermodulen verwendet werden können, explizit in einer Reihe von Symbollistendateien aufgeführt, die öffentlich im Kernelbaum verwaltet werden (gki/{ARCH}/symbols/*
oder android/abi_gki_{ARCH}_*
in Android 15 und niedriger). Die Vereinigung aller Symbole in allen Symbollistendateien definiert die Menge der KMI-Symbole, die als stabil gelten. Ein Beispiel für eine Symboldatei ist gki/aarch64/symbols/db845c
, in der die für das DragonBoard 845c erforderlichen Symbole deklariert werden.
Nur die in einer Symbolliste aufgeführten Symbole und die zugehörigen Strukturen und Definitionen sind Teil des KMI. Sie können Änderungen an Ihren Symbollisten vornehmen, wenn die benötigten Symbole nicht vorhanden sind. Nachdem neue Schnittstellen in einer Symbolliste enthalten sind und Teil der KMI-Beschreibung sind, werden sie als stabil beibehalten und dürfen nach dem Einfrieren des Zweigs nicht aus der Symbolliste entfernt oder geändert werden.
Jeder ACK-KMI-Kernel-Branch (Android Common Kernel) hat eigene Symbollisten. Es wird nicht versucht, die ABI-Stabilität zwischen verschiedenen KMI-Kernelzweigen aufrechtzuerhalten. Der KMI für android12-5.10
ist beispielsweise völlig unabhängig vom KMI für android13-5.10
.
ABI-Tools verwenden KMI-Symbollisten, um einzuschränken, welche Schnittstellen auf Stabilität überwacht werden müssen. Von Anbietern wird erwartet, dass sie ihre eigenen Symbollisten einreichen und aktualisieren, um zu überprüfen, ob die von ihnen verwendeten Schnittstellen die ABI-Kompatibilität beibehalten. Eine Liste der Symbollisten für den android16-6.12
-Kernel finden Sie beispielsweise unter https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols
.
Eine Symbolliste enthält die Symbole, die für den jeweiligen Anbieter oder das jeweilige Gerät benötigt werden. Die vollständige Liste, die von den Tools verwendet wird, ist die Vereinigung aller KMI-Symbollistendateien. ABI-Tools ermitteln die Details der einzelnen Symbole, einschließlich Funktionssignatur und verschachtelter Datenstrukturen.
Wenn das KMI eingefroren ist, sind keine Änderungen an den vorhandenen KMI-Schnittstellen zulässig. Sie sind stabil. Anbieter können dem KMI jedoch jederzeit Symbole hinzufügen, sofern die Stabilität des vorhandenen ABI nicht beeinträchtigt wird. Neu hinzugefügte Symbole bleiben stabil, sobald sie in einer KMI-Symbolliste aufgeführt werden. Symbole sollten nicht aus einer Liste für einen Kernel entfernt werden, es sei denn, es kann bestätigt werden, dass noch nie ein Gerät mit einer Abhängigkeit von diesem Symbol ausgeliefert wurde.
Sie können eine KMI-Symbolliste für ein Gerät gemäß der Anleitung unter Mit Symbollisten arbeiten erstellen. Viele Partner senden eine Symbolliste pro ACK, dies ist jedoch keine zwingende Anforderung. Wenn es die Wartung erleichtert, können Sie mehrere Symbollisten einreichen.
KMI verlängern
KMI-Symbole und zugehörige Strukturen bleiben stabil. Das bedeutet, dass Änderungen, die stabile Schnittstellen in einem Kernel mit einem eingefrorenen KMI beeinträchtigen, nicht akzeptiert werden können. Der GKI-Kernel bleibt jedoch offen für Erweiterungen, sodass Geräte, die später im Jahr ausgeliefert werden, nicht alle ihre Abhängigkeiten definieren müssen, bevor das KMI eingefroren wird. Sie können das KMI erweitern, indem Sie dem KMI neue Symbole für neue oder vorhandene exportierte Kernelfunktionen hinzufügen, auch wenn das KMI eingefroren ist. Neue Kernel-Patches werden möglicherweise auch akzeptiert, wenn sie das KMI nicht beeinträchtigen.
Informationen zu KMI-Fehlern
Ein Kernel hat Quellen und Binärdateien werden aus diesen Quellen erstellt.
ABI-überwachte Kernel-Branches enthalten eine ABI-Darstellung des aktuellen GKI-ABI (in Form einer .stg
-Datei). Nachdem die Binärdateien (vmlinux
, Image
und alle GKI-Module) erstellt wurden, kann eine ABI-Darstellung aus den Binärdateien extrahiert werden. Jede Änderung an einer Kernel-Quelldatei kann sich auf die Binärdateien und damit auch auf die extrahierten .stg
auswirken. Bei der ABI-Kompatibilitätsanalyse wird die committete .stg
-Datei mit der aus Build-Artefakten extrahierten Datei verglichen. Wenn ein semantischer Unterschied gefunden wird, wird die Änderung in Gerrit mit dem Label „Lint-1“ versehen.
ABI-Änderungen verarbeiten
Das folgende Beispiel zeigt einen Patch, der einen sehr offensichtlichen ABI-Bruch einführt:
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.
Wenn Sie „build ABI“ ausführen, nachdem dieser Patch angewendet wurde, wird das Tooling mit einem Fehlercode ungleich null beendet und meldet einen ABI-Unterschied, der in etwa so aussieht:
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
ABI-Unterschiede zur Build-Zeit erkannt
Der häufigste Grund für Fehler ist, wenn ein Treiber ein neues Symbol aus dem Kernel verwendet, das in keiner der Symbollisten enthalten ist.
Wenn das Symbol nicht in Ihrer Symbolliste enthalten ist, müssen Sie zuerst prüfen, ob es mit EXPORT_SYMBOL_GPL(symbol_name)
exportiert wurde. Aktualisieren Sie dann die Symbolliste und die ABI-Darstellung. Die folgenden Änderungen fügen beispielsweise dem android-12-5.10
-Branch das neue inkrementelle Dateisystem-Feature hinzu. Dazu müssen die Symbolliste und die ABI-Darstellung aktualisiert werden.
- Ein Beispiel für eine Featureänderung finden Sie unter aosp/1345659.
- Ein Beispiel für eine Symbolliste finden Sie unter aosp/1346742.
- Ein Beispiel für eine Änderung der ABI-Darstellung finden Sie unter aosp/1349377.
Wenn das Symbol exportiert wird (entweder von Ihnen oder es wurde bereits exportiert), aber kein anderer Treiber es verwendet, erhalten Sie möglicherweise einen Build-Fehler wie den folgenden.
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
Aktualisieren Sie zur Behebung des Problems die KMI-Symbolliste sowohl in Ihrem Kernel als auch im ACK (siehe ABI-Darstellung aktualisieren). Ein Beispiel für das Aktualisieren einer Symbolliste und der ABI-Darstellung im ACK finden Sie unter aosp/1367601.
Kernel-ABI-Fehler beheben
Sie können Kernel-ABI-Änderungen beheben, indem Sie den Code so umgestalten, dass sich das ABI nicht ändert, oder die ABI-Darstellung aktualisieren. Anhand des folgenden Diagramms können Sie den besten Ansatz für Ihre Situation ermitteln.
Abbildung 1: ABI-Fehlerbehebung
Code refaktorieren, um ABI-Änderungen zu vermeiden
Versuchen Sie, das vorhandene ABI nicht zu ändern. In vielen Fällen können Sie Ihren Code umgestalten, um Änderungen zu entfernen, die sich auf die ABI auswirken.
Änderungen an Struct-Feldern umgestalten Wenn durch eine Änderung die ABI für eine Debugging-Funktion geändert wird, fügen Sie ein
#ifdef
um die Felder (in den Strukturen und Quellreferenzen) hinzu und achten Sie darauf, dass das für das#ifdef
verwendeteCONFIG
für die Produktions-Defconfig undgki_defconfig
deaktiviert ist. Ein Beispiel dafür, wie eine Debug-Konfiguration zu einer Struktur hinzugefügt werden kann, ohne die ABI zu beschädigen, finden Sie in diesem Patchset.Refactoring von Funktionen, um den Hauptkernel nicht zu ändern: Wenn dem ACK neue Funktionen hinzugefügt werden müssen, um die Partnermodule zu unterstützen, versuchen Sie, den ABI-Teil der Änderung umzugestalten, um eine Änderung des Kernel-ABI zu vermeiden. Ein Beispiel für die Verwendung des vorhandenen Kernel-ABI zum Hinzufügen zusätzlicher Funktionen ohne Änderung des Kernel-ABI finden Sie unter aosp/1312213.
Fehlerhaftes ABI in Android Gerrit korrigieren
Wenn Sie die Kernel-ABI nicht absichtlich unterbrochen haben, müssen Sie die Anleitung des ABI-Monitoring-Tools verwenden, um die Ursache zu ermitteln. Die häufigsten Ursachen für Fehler sind Änderungen an Datenstrukturen und die damit verbundenen Änderungen an der Symbol-CRC oder Änderungen an Konfigurationsoptionen, die zu den oben genannten Problemen führen. Beginnen Sie damit, die vom Tool gefundenen Probleme zu beheben.
Sie können die ABI-Ergebnisse lokal reproduzieren. Weitere Informationen finden Sie unter Kernel und seine ABI-Darstellung erstellen.
Lint-1-Labels
Wenn Sie Änderungen an einem Branch mit einem eingefrorenen oder finalisierten KMI hochladen, müssen die Änderungen die ABI-Compliance- und -Kompatibilitätsanalysen bestehen, damit die Änderungen an der ABI-Darstellung das tatsächliche ABI widerspiegeln und keine Inkompatibilitäten (Entfernung von Symbolen oder Typänderungen) enthalten.
Bei jeder dieser ABI-Analysen kann das Label „Lint-1“ festgelegt und die Einreichung von Änderungen blockiert werden, bis alle Probleme behoben sind oder das Label überschrieben wird.
Kernel-ABI aktualisieren
Wenn eine Änderung der ABI unvermeidlich ist, müssen Sie die Codeänderungen, die ABI-Darstellung und die Symbolliste auf die Bestätigung anwenden. So können Sie Lint dazu bringen, die -1 zu entfernen, ohne die GKI-Kompatibilität zu beeinträchtigen:
Warten Sie, bis Sie für das Patchset eine Code-Review +2 erhalten.
Führen Sie Ihre Codeänderungen und die ABI-Aktualisierung zusammen.
ABI-Codeänderungen in die ACK hochladen
Die Aktualisierung des ACK-ABI hängt von der Art der Änderung ab.
Wenn sich eine ABI-Änderung auf eine Funktion bezieht, die sich auf CTS- oder VTS-Tests auswirkt, kann die Änderung in der Regel unverändert für ACK ausgewählt werden. Beispiel:
- aosp/1289677 ist erforderlich, damit Audio funktioniert.
- aosp/1295945 ist für die USB-Funktion erforderlich.
Wenn eine ABI-Änderung für eine Funktion erfolgt, die mit dem ACK geteilt werden kann, kann diese Änderung unverändert in das ACK übernommen werden. Die folgenden Änderungen sind beispielsweise für CTS- oder VTS-Tests nicht erforderlich, können aber mit dem ACK geteilt werden:
- aosp/1250412 ist eine Änderung der thermischen Funktion.
- aosp/1288857 ist eine
EXPORT_SYMBOL_GPL
-Änderung.
Wenn durch eine ABI-Änderung eine neue Funktion eingeführt wird, die nicht in die Bestätigung aufgenommen werden muss, können Sie die Symbole mit einem Stub in die Bestätigung aufnehmen, wie im folgenden Abschnitt beschrieben.
Stubs für ACK verwenden
Stubs dürfen nur für wichtige Kerneländerungen erforderlich sein, die nicht vom ACK profitieren, z. B. Leistungs- und Energieänderungen. In der folgenden Liste finden Sie Beispiele für Stubs und teilweise Cherry-Picks in ACK für GKI.
Core-Isolate-Funktions-Stub (aosp/1284493). Die Funktionen in ACK sind nicht erforderlich, aber die Symbole müssen in ACK vorhanden sein, damit Ihre Module diese Symbole verwenden können.
Platzhaltersymbol für das Anbietermodul (aosp/1288860).
Nur ABI-Cherry-Pick der Funktion zum Erfassen von
mm
-Ereignissen pro Prozess (aosp/1288454). Der ursprüngliche Patch wurde für ACK ausgewählt und dann so gekürzt, dass er nur die notwendigen Änderungen zur Behebung des ABI-Unterschieds fürtask_struct
undmm_event_count
enthält. Mit diesem Patch wird auch dasmm_event_type
-Enum mit den endgültigen Mitgliedern aktualisiert.Teilweises Cherry-Picking von ABI-Änderungen an der Thermostruktur, die mehr als nur das Hinzufügen der neuen ABI-Felder erforderten.
Mit dem Patch aosp/1255544 wurden ABI-Unterschiede zwischen dem Partner-Kernel und dem ACK behoben.
Mit dem Patch aosp/1291018 wurden die funktionalen Probleme behoben, die bei den GKI-Tests des vorherigen Patches gefunden wurden. Der Fehler wurde behoben, indem die Sensorparameterstruktur initialisiert wurde, um mehrere thermische Zonen für einen einzelnen Sensor zu registrieren.
CONFIG_NL80211_TESTMODE
ABI-Änderungen (aosp/1344321). Mit diesem Patch wurden die erforderlichen Strukturänderungen für die ABI hinzugefügt und dafür gesorgt, dass die zusätzlichen Felder keine funktionalen Unterschiede verursachen. So können PartnerCONFIG_NL80211_TESTMODE
in ihre Produktionskernel aufnehmen und trotzdem die GKI-Konformität beibehalten.
KMI zur Laufzeit erzwingen
Die GKI-Kernel verwenden die Konfigurationsoptionen TRIM_UNUSED_KSYMS=y
und UNUSED_KSYMS_WHITELIST=<union
of all symbol lists>
, die die exportierten Symbole (z. B. mit EXPORT_SYMBOL_GPL()
exportierte Symbole) auf die in einer Symbolliste beschränken. Alle anderen Symbole werden nicht exportiert und das Laden eines Moduls, das ein nicht exportiertes Symbol erfordert, wird abgelehnt. Diese Einschränkung wird zur Build-Zeit erzwungen und fehlende Einträge werden gekennzeichnet.
Für Entwicklungszwecke können Sie einen GKI-Kernel-Build verwenden, der kein Symbol-Trimming enthält. Das bedeutet, dass alle normalerweise exportierten Symbole verwendet werden können. Suchen Sie auf ci.android.com nach den kernel_debug_aarch64
-Builds.
KMI mit Modulversionierung erzwingen
Die GKI-Kernel (Generic Kernel Image) verwenden Modulversionierung (CONFIG_MODVERSIONS
) als zusätzliche Maßnahme, um die KMI-Konformität zur Laufzeit zu erzwingen. Die Versionsverwaltung von Modulen kann beim Laden von Modulen zu CRC-Fehlern (Cyclic Redundancy Check) führen, wenn das erwartete KMI eines Moduls nicht mit dem vmlinux
-KMI übereinstimmt. Das folgende Beispiel zeigt einen typischen Fehler, der beim Laden des Moduls aufgrund einer CRC-Fehlerübereinstimmung für das Symbol module_layout()
auftritt:
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 ''
Verwendung der Modulversionsverwaltung
Die Versionsverwaltung von Modulen ist aus folgenden Gründen nützlich:
Durch die Modulversionsverwaltung werden Änderungen an der Sichtbarkeit der Datenstruktur erfasst. Wenn Module undurchsichtige Datenstrukturen ändern, d. h. Datenstrukturen, die nicht Teil des KMI sind, funktionieren sie nach zukünftigen Änderungen an der Struktur nicht mehr.
Ein Beispiel ist das Feld
fwnode
instruct device
. Dieses Feld MUSS für Module undurchsichtig sein, damit sie keine Änderungen an Feldern vondevice->fw_node
vornehmen oder Annahmen über die Größe treffen können.Wenn ein Modul jedoch
<linux/fwnode.h>
(direkt oder indirekt) enthält, ist das Feldfwnode
instruct device
nicht mehr undurchsichtig. Das Modul kann dann Änderungen andevice->fwnode->dev
oderdevice->fwnode->ops
vornehmen. Dieses Szenario ist aus mehreren Gründen problematisch:Dadurch können Annahmen verletzt werden, die der Kernkernelcode über seine internen Datenstrukturen trifft.
Wenn sich durch ein zukünftiges Kernel-Update die
struct fwnode_handle
(der Datentyp vonfwnode
) ändert, funktioniert das Modul nicht mehr mit dem neuen Kernel. Außerdem werden instgdiff
keine Unterschiede angezeigt, da das Modul das KMI durch direkte Manipulation interner Datenstrukturen auf eine Weise unterbricht, die nicht nur durch die binäre Darstellung erfasst werden kann.
Ein aktuelles Modul gilt als KMI-inkompatibel, wenn es zu einem späteren Zeitpunkt von einem neuen, inkompatiblen Kernel geladen wird. Durch die Modulversionierung wird eine Laufzeitprüfung hinzugefügt, um zu verhindern, dass versehentlich ein Modul geladen wird, das nicht KMI-kompatibel mit dem Kernel ist. Diese Prüfung verhindert schwer zu debuggende Laufzeitprobleme und Kernelabstürze, die durch eine nicht erkannte Inkompatibilität im KMI entstehen können.
Wenn Sie die Modulversionsverwaltung aktivieren, werden alle diese Probleme vermieden.
CRC-Abweichungen prüfen, ohne das Gerät zu starten
stgdiff
vergleicht und meldet CRC-Abweichungen zwischen Kernels sowie andere ABI-Unterschiede.
Außerdem wird bei einem vollständigen Kernel-Build mit aktivierter CONFIG_MODVERSIONS
-Option im Rahmen des normalen Build-Prozesses eine Module.symvers
-Datei generiert. Diese Datei enthält eine Zeile für jedes Symbol, das vom Kernel (vmlinux
) und den Modulen exportiert wird. Jede Zeile besteht aus dem CRC-Wert, dem Symbolnamen, dem Symbol-Namespace, dem vmlinux
oder dem Modulnamen, der das Symbol exportiert, und dem Exporttyp (z. B. EXPORT_SYMBOL
im Vergleich zu EXPORT_SYMBOL_GPL
).
Sie können die Module.symvers
-Dateien zwischen dem GKI-Build und Ihrem Build vergleichen, um nach CRC-Unterschieden bei den von vmlinux
exportierten Symbolen zu suchen. Wenn es in einem von vmlinux
exportierten Symbol einen CRC-Wertunterschied gibt und dieses Symbol von einem der Module verwendet wird, die Sie auf Ihr Gerät laden, wird das Modul nicht geladen.
Wenn Sie nicht alle Build-Artefakte, aber die vmlinux
-Dateien des GKI-Kernels und Ihres Kernels haben, können Sie die CRC-Werte für ein bestimmtes Symbol vergleichen, indem Sie den folgenden Befehl für beide Kernel ausführen und die Ausgabe vergleichen:
nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>
Mit dem folgenden Befehl wird beispielsweise der CRC-Wert für das Symbol module_layout
geprüft:
nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout
CRC-Abweichungen beheben
So beheben Sie eine CRC-Fehler beim Laden eines Moduls:
Erstellen Sie den GKI-Kernel und den Geräte-Kernel mit der Option
--kbuild_symtypes
, wie im folgenden Befehl gezeigt:tools/bazel run --kbuild_symtypes //common:kernel_aarch64_dist
Mit diesem Befehl wird für jede
.o
-Datei eine.symtypes
-Datei generiert. Weitere Informationen finden Sie unterKBUILD_SYMTYPES
in Kleaf.Für Android 13 und niedriger erstellen Sie den GKI-Kernel und den Geräte-Kernel, indem Sie dem Befehl, den Sie zum Erstellen des Kernels verwenden,
KBUILD_SYMTYPES=1
voranstellen, wie im folgenden Befehl gezeigt:KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
Wenn Sie
build_abi.sh,
verwenden, wird das FlagKBUILD_SYMTYPES=1
implizit gesetzt.Suchen Sie mit dem folgenden Befehl nach der
.c
-Datei, in die das Symbol mit CRC-Fehler exportiert wurde:git -C common grep EXPORT_SYMBOL.*module_layout kernel/module/version.c:EXPORT_SYMBOL(module_layout);
Die Datei
.c
hat eine entsprechende.symtypes
-Datei im GKI und in den Build-Artefakten des Geräte-Kernels. Suchen Sie mit den folgenden Befehlen nach der Datei.symtypes
:cd bazel-bin/common/kernel_aarch64/symtypes ls -1 kernel/module/version.symtypes
Unter Android 13 und niedriger ist der Speicherort bei Verwendung der alten Build-Scripts wahrscheinlich entweder
out/$BRANCH/common
oderout_abi/$BRANCH/common
.Jede
.symtypes
-Datei ist eine Nur-Text-Datei, die aus Typ- und Symbolbeschreibungen besteht:Jede Zeile hat das Format
key description
, wobei sich die Beschreibung auf andere Schlüssel in derselben Datei beziehen kann.Schlüssel wie
[s|u|e|t]#foo
verweisen auf[struct|union|enum|typedef] foo
. Beispiel:t#bool typedef _Bool bool
Schlüssel ohne das Präfix
x#
sind nur Symbolnamen. Beispiel:find_module s#module * find_module ( const char * )
Vergleichen Sie die beiden Dateien und beheben Sie alle Unterschiede.
symtypes
sollte am besten mit einem Build kurz vor der problematischen Änderung und dann bei der problematischen Änderung generiert werden. Wenn Sie alle Dateien speichern, können sie im Bulk-Verfahren verglichen werden.
Beispiel:
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
Andernfalls vergleichen Sie einfach die spezifischen Dateien, die Sie interessieren.
Fall 1: Unterschiede aufgrund der Sichtbarkeit des Datentyps
Mit einem neuen #include
kann eine neue Typdefinition (z. B. von struct foo
) in eine Quelldatei eingefügt werden. In diesen Fällen ändert sich die Beschreibung in der entsprechenden .symtypes
-Datei von einem leeren structure_type foo { }
zu einer vollständigen Definition.
Dies wirkt sich auf alle CRCs aller Symbole in der Datei .symtypes
aus, deren Beschreibungen direkt oder indirekt von der Typdefinition abhängen.
Wenn Sie beispielsweise die folgende Zeile in die Datei include/linux/device.h
in Ihrem Kernel einfügen, führt dies zu CRC-Fehlern, einer davon für module_layout()
:
#include <linux/fwnode.h>
Beim Vergleich der module/version.symtypes
für dieses Symbol werden die folgenden Unterschiede deutlich:
$ 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 ] ; }
...
Wenn der GKI-Kernel die vollständige Typdefinition hat, in Ihrem Kernel diese jedoch fehlt (sehr unwahrscheinlich), führen Sie den neuesten Android Common Kernel in Ihren Kernel ein, damit Sie die neueste GKI-Kernelbasis verwenden.
In den meisten Fällen fehlt im GKI-Kernel die vollständige Typdefinition in .symtypes
, aber in Ihrem Kernel ist sie aufgrund zusätzlicher #include
-Anweisungen vorhanden.
Auflösung für Android 16 und höher
Prüfen Sie, ob die betroffene Quelldatei den Header für die Android-KABI-Stabilisierung enthält:
#include <linux/android_kabi.h>
Fügen Sie für jeden betroffenen Typ ANDROID_KABI_DECLONLY(name);
im globalen Bereich in die betroffene Quelldatei ein.
Wenn der symtypes
-Unterschied beispielsweise so aussah:
--- 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)
Das Problem besteht dann darin, dass struct ubuf_info
jetzt eine vollständige Definition in symtypes
hat. Die Lösung besteht darin, drivers/android/vendor_hooks.c
eine Zeile hinzuzufügen:
ANDROID_KABI_DECLONLY(ubuf_info);
Dadurch wird gendwarfksyms
angewiesen, den benannten Typ in der Datei als nicht definiert zu behandeln.
Eine komplexere Möglichkeit ist, dass sich das neue #include
selbst in einer Headerdatei befindet. In diesem Fall müssen Sie möglicherweise verschiedene Gruppen von ANDROID_KABI_DECLONLY
-Makroaufrufen auf Quelldateien verteilen, die indirekt zusätzliche Typdefinitionen abrufen, da einige von ihnen möglicherweise bereits einige der Typdefinitionen enthalten.
Um die Lesbarkeit zu verbessern, sollten Sie solche Makroaufrufe in der Nähe des Anfangs der Quelldatei platzieren.
Auflösung für Android 15 und niedriger
Häufig besteht die Lösung darin, die neue #include
in genksyms
auszublenden.
#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif
Andernfalls können Sie so vorgehen, um die #include
zu ermitteln, die die Abweichung verursacht:
Öffnen Sie die Headerdatei, in der das Symbol oder der Datentyp mit diesem Unterschied definiert ist. Bearbeiten Sie beispielsweise
include/linux/fwnode.h
fürstruct fwnode_handle
.Fügen Sie oben in der Headerdatei den folgenden Code ein:
#ifdef CRC_CATCH #error "Included from here" #endif
Fügen Sie in der
.c
-Datei des Moduls mit einem CRC-Fehler die folgende Zeile als erste Zeile vor allen#include
-Zeilen ein.#define CRC_CATCH 1
Kompilieren Sie Ihr Modul. Der resultierende Build-Zeit-Fehler zeigt die Kette der Headerdatei
#include
, die zu dieser CRC-Fehler führte. Beispiel: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"
Einer der Links in dieser Kette von
#include
ist auf eine Änderung in Ihrem Kernel zurückzuführen, die im GKI-Kernel fehlt.
Fall 2: Unterschiede aufgrund von Änderungen des Datentyps
Wenn die CRC-Abweichung für ein Symbol oder einen Datentyp nicht auf einen Unterschied in der Sichtbarkeit zurückzuführen ist, liegt das an tatsächlichen Änderungen (Hinzufügungen, Entfernungen oder Änderungen) am Datentyp selbst.
Wenn Sie beispielsweise die folgende Änderung in Ihrem Kernel vornehmen, führt dies zu mehreren CRC-Fehlern, da viele Symbole indirekt von dieser Art von Änderung betroffen sind:
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);
Eine CRC-Abweichung betrifft devm_of_platform_populate()
.
Wenn Sie die .symtypes
-Dateien für dieses Symbol vergleichen, sieht das möglicherweise so aus:
$ 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 * ) ; ...
So ermitteln Sie den geänderten Typ:
Suchen Sie im Quellcode nach der Definition des Symbols (normalerweise in
.h
-Dateien).- Wenn es Unterschiede bei den Symbolen zwischen Ihrem Kernel und dem GKI-Kernel gibt, finden Sie den Commit mit dem folgenden Befehl:
git blame
- Bei gelöschten Symbolen (wenn ein Symbol in einem Baum gelöscht wird und Sie es auch im anderen Baum löschen möchten) müssen Sie die Änderung finden, durch die die Zeile gelöscht wurde. Verwenden Sie den folgenden Befehl für den Baum, in dem die Zeile gelöscht wurde:
git log -S "copy paste of deleted line/word" -- <file where it was deleted>
Sehen Sie sich die zurückgegebene Liste mit Commits an, um die Änderung oder Löschung zu finden. Der erste Commit ist wahrscheinlich der, den Sie suchen. Wenn nicht, durchsuchen Sie die Liste, bis Sie den Commit finden.
Nachdem Sie den Commit identifiziert haben, können Sie ihn entweder in Ihrem Kernel zurücksetzen oder aktualisieren, um die CRC-Änderung zu unterdrücken, und ihn in ACK hochladen und zusammenführen lassen. Jede verbleibende ABI-Unterbrechung muss auf Sicherheit geprüft werden. Falls erforderlich, kann eine zulässige Unterbrechung aufgezeichnet werden.
Vorhandenes Padding bevorzugen
Einige Strukturen in GKI werden aufgefüllt, um ihre Erweiterung zu ermöglichen, ohne vorhandene Anbietermodule zu beschädigen. Wenn durch einen Upstream-Commit (z. B.) ein Mitglied zu einer solchen Struktur hinzugefügt wird, kann es möglich sein, die Struktur so zu ändern, dass stattdessen ein Teil des Padding verwendet wird. Diese Änderung wird dann bei der CRC-Berechnung ausgeblendet.
Das standardisierte, selbstdokumentierende Makro ANDROID_KABI_RESERVE
reserviert einen (ausgerichteten) Platz im Wert von u64
. Sie wird anstelle einer Member-Deklaration verwendet.
Beispiel:
struct data {
u64 handle;
ANDROID_KABI_RESERVE(1);
ANDROID_KABI_RESERVE(2);
};
Die Auffüllung kann mit ANDROID_KABI_USE
(oder ANDROID_KABI_USE2
oder anderen Varianten, die möglicherweise definiert sind) verarbeitet werden, ohne dass sich dies auf die Symbol-CRCs auswirkt.
Das Mitglied sekret
ist verfügbar, als wäre es direkt deklariert. Das Makro wird jedoch zu einem anonymen Union-Mitglied erweitert, das sekret
sowie Elemente enthält, die von gendwarfksyms
zur Aufrechterhaltung der Symtype-Stabilität verwendet werden.
struct data {
u64 handle;
ANDROID_KABI_USE(1, void *sekret);
ANDROID_KABI_RESERVE(2);
};
Auflösung für Android 16 und höher
CRCs werden von gendwarfksyms
berechnet, das DWARF-Debugging-Informationen verwendet und daher sowohl C- als auch Rust-Typen unterstützt. Die Auflösung variiert je nach Art der Typänderung. Hier einige Beispiele:
Neue oder geänderte Enumeratoren
Manchmal werden neue Enumeratoren hinzugefügt und gelegentlich ist auch ein MAX
- oder ähnlicher Enumeratorwert betroffen. Diese Änderungen sind sicher, wenn sie nicht aus dem GKI „entkommen“ oder wenn wir sicher sein können, dass sich Anbietermodule nicht um ihre Werte kümmern können.
Beispiel:
enum outcome {
SUCCESS,
FAILURE,
RETRY,
+ TRY_HARDER,
OUTCOME_LIMIT
};
Das Hinzufügen von TRY_HARDER
und die Änderung von OUTCOME_LIMIT
können bei der CRC-Berechnung durch Makroaufrufe im globalen Bereich ausgeblendet werden:
ANDROID_KABI_ENUMERATOR_IGNORE(outcome, TRY_HARDER);
ANDROID_KABI_ENUMERATOR_VALUE(outcome, OUTCOME_LIMIT, 3);
Um die Lesbarkeit zu verbessern, sollten Sie diese direkt nach der enum
-Definition platzieren.
Ein neues Strukturelement, das ein vorhandenes Loch ausfüllt
Aufgrund der Ausrichtung gibt es zwischen urgent
und scratch
ungenutzte Bytes.
void *data;
bool urgent;
+ bool retry;
void *scratch;
Weder der vorhandene Mitglieds-Offset noch die Größe der Struktur werden durch das Hinzufügen von retry
beeinflusst. Dies kann sich jedoch auf die Symbol-CRCs oder die ABI-Darstellung oder beides auswirken.
Dadurch wird sie bei der CRC-Berechnung ausgeblendet:
void *data;
bool urgent;
+ ANDROID_KABI_IGNORE(1, bool retry);
void *scratch_space;
Das Mitglied retry
ist verfügbar, als wäre es direkt deklariert. Das Makro wird jedoch zu einem anonymen Union-Mitglied erweitert, das retry
sowie Elemente enthält, die von gendwarfksyms
zur Aufrechterhaltung der Symtype-Stabilität verwendet werden.
Erweiterung einer Struktur mit neuen Mitgliedern
Mitglieder werden manchmal am Ende einer Struktur hinzugefügt. Das hat keine Auswirkungen auf die Offsets vorhandener Mitglieder oder auf vorhandene Nutzer der Struktur, die nur über einen Zeiger darauf zugreifen. Die Größe der Struktur wirkt sich auf ihren CRC aus. Änderungen daran können durch einen zusätzlichen Makroaufruf im globalen Bereich unterdrückt werden:
struct data {
u64 handle;
u64 counter;
ANDROID_KABI_IGNORE(1, void *sekret);
};
ANDROID_KABI_BYTE_SIZE(data, 16);
Um die Lesbarkeit zu verbessern, platzieren Sie diesen Code direkt nach der struct
-Definition.
Alle anderen Änderungen an einem Typ oder dem Typ eines Symbols
In seltenen Fällen kann es zu Änderungen kommen, die nicht in eine der vorherigen Kategorien fallen. Dies führt zu CRC-Änderungen, die mit den vorherigen Makros nicht unterdrückt werden können.
In diesen Fällen kann die ursprüngliche symtypes
-Beschreibung eines Typs oder Symbols durch einen Aufruf von ANDROID_KABI_TYPE_STRING
im globalen Bereich ergänzt werden.
struct data {
/* extensive changes */
};
ANDROID_KABI_TYPE_STRING("s#data", "original s#data symtypes definition");
Zur besseren Lesbarkeit sollte dies direkt nach der Typ- oder Symboldefinition erfolgen.
Auflösung für Android 15 und niedriger
Änderungen des Typs und des Symboltyps müssen vor genksyms
verborgen werden. Dazu können Sie die Vorverarbeitung mit __GENKSYMS__
steuern.
So lassen sich beliebige Code-Transformationen ausdrücken.
So blenden Sie beispielsweise ein neues Mitglied aus, das eine Lücke in einer bestehenden Struktur füllt:
struct parcel {
void *data;
bool urgent;
#ifndef __GENKSYMS__
bool retry;
#endif
void *scratch_space;
};