Monitorowanie ABI jądra Androida

Aby ustabilizować interfejs ABI wewnątrz jądra w jądrach Androida, możesz użyć narzędzia do monitorowania interfejsu binarnego aplikacji (ABI), dostępnego w Androidzie 11 i nowszych. Narzędzie zbiera i porównuje reprezentacje ABI z dotychczasowych binarnych plików jądra (vmlinux + moduły GKI). Te reprezentacje ABI to pliki .stg i listy symboli. Interfejs, w którym reprezentacja udostępnia widok, nazywa się interfejsem modułu jądra (KMI). Za pomocą tych narzędzi możesz śledzić i ograniczać zmiany w KMI.

Narzędzia do monitorowania ABI są opracowywane w AOSP i korzystają z STG (lub libabigail w Androidzie 13 i starszych), aby generować i porównywać reprezentacje.

Na tej stronie opisaliśmy narzędzia, proces zbierania i analizowania reprezentacji ABI oraz wykorzystanie tych reprezentacji do zapewnienia stabilności interfejsu ABI w jądrze. Na tej stronie znajdziesz też informacje o wprowadzaniu zmian w jądrach Androida.

Proces

Analiza ABI jądra wymaga wykonania kilku czynności, z których większość można zautomatyzować:

  1. Utwórz jądro i jego reprezentacja ABI.
  2. Analizować różnice w ABI między kompilacją a plikiem referencyjnym.
  3. Zaktualizuj reprezentację ABI (w razie potrzeby).
  4. Praca z listami symboli.

Podane niżej instrukcje dotyczą dowolnego jądra, które możesz skompilować za pomocą obsługiwanego zestawu narzędzi (np. wstępnie skompilowanego zestawu narzędzi Clang). repo manifestssą dostępne dla wszystkich gałęzi jądra Androida i dla kilku jąder przeznaczonych do konkretnych urządzeń. Umożliwiają one używanie prawidłowej łańcucha narzędzi podczas kompilowania dystrybucji jądra na potrzeby analizy.

Listy symboli

KMI nie obejmuje wszystkich symboli w jądrze ani nawet wszystkich 30 tys. wyeksportowanych symboli. Zamiast tego symbole, których mogą używać moduły dostawców, są wyraźnie wymienione w zbiorze plików listy symboli, który jest publicznie dostępny w drzewie jądra (gki/{ARCH}/symbols/* lub android/abi_gki_{ARCH}_* w Androidzie 15 i starszych). Zbiór wszystkich symboli we wszystkich plikach listy symboli definiuje zbiór symboli KMI, które są utrzymywane jako stabilne. Przykładowy plik z listą symboli to gki/aarch64/symbols/db845c, który zawiera symbole wymagane w przypadku urządzenia DragonBoard 845c.

Do KMI zalicza się tylko symbole wymienione na liście symboli oraz powiązane z nimi struktury i definicje. Jeśli brakuje Ci potrzebnych symboli, możesz wprowadzić zmiany w listach symboli. Gdy nowe interfejsy zostaną dodane do listy symboli i staną się częścią opisu KMI, będą traktowane jako stabilne. Nie można ich usuwać z listy symboli ani modyfikować po zamrożeniu gałęzi.

Każda gałąź jądra KMI w ramach wspólnego jądra Androida (ACK) ma własny zestaw list symboli. Nie podejmuje się prób zapewnienia stabilności ABI między różnymi gałęziami jądra KMI. Na przykład KMI dla android12-5.10 jest całkowicie niezależny od KMI dla android13-5.10.

Narzędzia ABI korzystają z list symboli KMI, aby ograniczyć liczbę interfejsów, które należy monitorować pod kątem stabilności. Dostawcy powinni przesyłać i aktualizować własne listy symboli, aby zapewnić zgodność interfejsów, których używają, z interfejsami ABI. Aby na przykład wyświetlić listę list symboli dla jądra android16-6.12, przejdź do https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols.

Lista symboli zawiera symbole, które są potrzebne w przypadku konkretnego dostawcy lub urządzenia. Pełna lista używana przez narzędzia jest zbiorem wszystkich plików listy symboli KMI. Narzędzia ABI określają szczegóły każdego symbolu, w tym sygnaturę funkcji i zagnieżdżone struktury danych.

Gdy interfejs KMI jest zamrożony, nie można wprowadzać zmian w dotychczasowych interfejsach KMI, ponieważ są one stabilne. Dostawcy mogą jednak dodawać symbole do KMI w dowolnym momencie, o ile nie wpłynie to na stabilność istniejącego ABI. Nowo dodane symbole są utrzymywane w stanie stabilnym od momentu, gdy są one wymienione na liście symboli KMI. Symboli nie należy usuwać z listy jądra, chyba że można potwierdzić, że żadne urządzenie nigdy nie było dostarczane z zależnością od tego symbolu.

Listę symboli KMI dla urządzenia możesz wygenerować, postępując zgodnie z instrukcjami podanymi w artykule Praca z listami symboli. Wielu partnerów przesyła jedną listę symboli na każdą odpowiedź, ale nie jest to rygorystyczne wymaganie. Jeśli ułatwi to konserwację, możesz przesłać kilka list symboli.

Rozszerzenie KMI

Chociaż symbole KMI i powiązane z nimi struktury są utrzymywane jako stabilne (co oznacza, że zmiany, które powodują niestabilność interfejsów w jądrze z zamrożonym KMI, nie mogą być akceptowane), jądro GKI pozostaje otwarte na rozszerzenia, aby urządzenia wysyłane w późniejszej części roku nie musiały definiować wszystkich swoich zależności przed zamrożeniem KMI. Aby rozszerzyć KMI, możesz dodać do niego nowe symbole dla nowych lub istniejących wyeksportowanych funkcji jądra, nawet jeśli KMI jest zamrożony. Nowe poprawki jądra mogą być również akceptowane, jeśli nie naruszają KMI.

Informacje o awariach KMI

Kernel ma źródła, na podstawie których tworzone są pliki binarne. Gałęzie jądra monitorowane pod kątem ABI zawierają reprezentację ABI bieżącego ABI GKI (w postaci pliku .stg). Po skompilowaniu plików binarnych (vmlinux, Image i wszystkich modułów GKI) można wyodrębnić z nich reprezentację ABI. Każda zmiana wprowadzona w pliku źródłowym jądra może wpłynąć na pliki binarne, a w efekcie także na wyodrębnione .stg. Analizator AbiAnalyzer porównuje zaakceptowany plik .stg z plikiem wyodrębnionym z elementów kompilacji i w przypadku wykrycia różnicy semantycznej ustawia etykietę Lint-1 dla zmiany w Gerrecie.

Rozwiązywanie problemów z interfejsem ABI

Na przykład ten pakiet poprawek wprowadza bardzo oczywiste naruszenie ABI:

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.

Gdy uruchomisz kompilację ABI z zaimplementowaną łatką, narzędzie zakończy działanie z niezerowym kodem błędu i zgłosi różnicę w ABI podobną do tej:

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

różnice w ABI wykryte w momencie kompilacji,

Najczęstszą przyczyną błędów jest użycie przez sterownik nowego symbolu z jądra, którego nie ma na żadnej z list symboli.

Jeśli symbol nie znajduje się na liście symboli, musisz najpierw sprawdzić, czy został wyeksportowany za pomocą funkcji EXPORT_SYMBOL_GPL(symbol_name), a następnie zaktualizować listę symboli i reprezentację ABI. Na przykład poniższe zmiany wprowadzają do gałęzi android-12-5.10 nową funkcję przyrostowego FS, która obejmuje aktualizację listy symboli i reprezentacji ABI.

  • Przykład zmiany funkcji znajduje się w aosp/1345659.
  • Przykład listy symboli znajduje się w pliku aosp/1346742.
  • Przykład zmiany reprezentacji ABI znajduje się w aosp/1349377.

Jeśli symbol jest wyeksportowany (przez Ciebie lub został wyeksportowany wcześniej), ale żaden inny sterownik z niego nie korzysta, może pojawić się błąd kompilacji podobny do tego.

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

Aby rozwiązać ten problem, zaktualizuj listę symboli KMI zarówno w jądrze, jak i w ACK (patrz Aktualizowanie reprezentacji ABI). Przykład aktualizacji listy symboli i reprezentacji ABI w ACK znajdziesz w artykule aosp/1367601.

Rozwiązywanie problemów z interfejsem ABI jądra

Możesz rozwiązać problem z naruszeniem ABI jądra, przekształcając kod, aby nie zmieniać ABI lub zaktualizować reprezentację ABI. Aby określić najlepsze podejście w danej sytuacji, użyj poniższej tabeli.

Schemat procesu w przypadku naruszenia interfejsu ABI

Rysunek 1. Rozwiązanie problemu z naruszeniem ABI

Refaktoryzuj kod, aby uniknąć zmian ABI

Dokładaj wszelkich starań, aby nie modyfikować istniejącego ABI. W wielu przypadkach możesz przerobić kod, aby usunąć zmiany, które wpływają na ABI.

  • Refaktoryzacja zmian pól struktury. Jeśli zmiana modyfikuje ABI funkcji debugowania, dodaj #ifdef wokół pól (w strukturach i odniesieniach do źródła) i upewnij się, że CONFIG używany w #ifdef jest wyłączony w defconfigie produkcyjnym i gki_defconfig. Przykład dodawania konfiguracji debugowania do struktury bez naruszania ABI znajdziesz w tym zestawie poprawek.

  • Refaktoryzacja funkcji, aby nie zmieniać jądra jądra. Jeśli do ACK trzeba dodać nowe funkcje, aby obsługiwać moduły partnera, spróbuj przerobić ABI w ramach zmiany, aby uniknąć modyfikowania ABI jądra. Przykład użycia istniejącego interfejsu ABI jądra w celu dodania dodatkowych funkcji bez zmiany interfejsu ABI jądra: aosp/1312213.

Naprawianie nieprawidłowego ABI w Gerrite na Androida

Jeśli nie doszło do celowego naruszenia ABI jądra, musisz zbadać problem, korzystając z wskazówek podanych przez narzędzie do monitorowania ABI. Najczęstsze przyczyny przerw w działaniu to zmiany struktur danych i powiązane z nimi zmiany CRC symboli lub zmiany opcji konfiguracji, które prowadzą do któregokolwiek z wymienionych wyżej przypadków. Zacznij od rozwiązania problemów znalezionych przez narzędzie.

Możesz odtworzyć wyniki dotyczące ABI lokalnie. W tym celu zobacz artykuł Tworzenie jądra i jego reprezentacja ABI.

Etykiety Lint-1

Jeśli prześlesz zmiany do gałęzi zawierającej zamrożoną lub sfinalizowaną KMI, zmiany muszą przejść weryfikację AbiAnalyzer, aby mieć pewność, że nie wpłyną one na stabilną ABI w niezgodny sposób. Podczas tego procesu AbiAnalyzer szuka raportu ABI utworzonego podczas kompilacji (rozszerzona kompilacja, która wykonuje normalną kompilację, a potem niektóre kroki ekstrakcji i porównywania ABI).

Jeśli AbiAnalyzer znajdzie niepusty raport, ustawi etykietę Lint-1, a zmianę zablokuje do momentu jej rozwiązania, czyli do momentu, gdy zestaw poprawek otrzyma etykietę Lint+1.

Aktualizacja ABI jądra

Jeśli modyfikacja ABI jest nieunikniona, musisz zastosować zmiany kodu, reprezentację ABI i listę symboli w ACK. Aby Lint usuwał -1, a nie łamał zgodności z GKI, wykonaj te czynności:

  1. Przesyłaj zmiany kodu do ACK.

  2. Poczekaj na otrzymanie oceny Code-Review +2 dla zestawu poprawek.

  3. Zaktualizuj referencyjną reprezentację ABI.

  4. Połącz zmiany kodu z aktualizacją ABI.

Prześlij zmiany kodu ABI do ACK

Aktualizacja ABI ACK zależy od typu wprowadzanej zmiany.

  • Jeśli zmiana ABI jest związana z funkcją, która wpływa na testy CTS lub VTS, można ją zwykle zastosować w zbieraniu opinii w niezmienionej formie. Przykłady:

    • Aby dźwięk działał, musisz mieć zainstalowaną wersję aosp/1289677.
    • aosp/1295945 jest wymagany do działania USB.
  • Jeśli zmiana ABI dotyczy funkcji, którą można udostępnić w wersji ACK, można ją zastosować w wersji ACK w postaci domyślnej. Na przykład te zmiany nie są wymagane do testu CTS ani VTS, ale można je udostępnić w wersji ACK:

  • Jeśli zmiana ABI wprowadza nową funkcję, która nie musi być uwzględniona w ACK, możesz wprowadzić symbole do ACK za pomocą zastępnika zgodnie z opisem w następującej sekcji.

Używanie zastępników w przypadku potwierdzenia

Stuby muszą być potrzebne tylko w przypadku zmian w rdzeniu jądra, które nie przynoszą korzyści ACK, takich jak zmiany wydajności i poboru mocy. Poniżej znajdziesz listę przykładów szkiców i częściowych elementów w ACK dla GKI.

  • Element szablonu funkcji izolowania funkcji podstawowych (aosp/1284493). Funkcje w ACK nie są wymagane, ale symbole muszą być obecne w ACK, aby moduły mogły z nich korzystać.

  • Symbol zastępczy dla modułu dostawcy (aosp/1288860).

  • Funkcja śledzenia zdarzeń mm (aosp/1288454) wybierana tylko na podstawie ABI. Oryginalny pakiet poprawek został wybrany do zaakceptowania, a następnie przycięty tak, aby zawierał tylko zmiany niezbędne do rozwiązania różnic w ABI w przypadku task_structmm_event_count. Ta poprawka aktualizuje też enumerację mm_event_type, aby zawierała ostatecznych członków.

  • Wybrane zmiany ABI struktury termicznej, które wymagały czegoś więcej niż tylko dodania nowych pól ABI.

    • Poprawka aosp/1255544 rozwiązała różnice w ABI między jądrem partnera a ACK.

    • Aktualizacja aosp/1291018rozwiązała problemy z funkcjonalnością, które zostały wykryte podczas testowania poprzedniej aktualizacji przez GKI. Naprawa obejmowała zainicjowanie struktury parametrów czujnika w celu zarejestrowania wielu stref termicznych dla jednego czujnika.

  • CONFIG_NL80211_TESTMODE zmiany ABI (aosp/1344321). Ta poprawka wprowadziła niezbędne zmiany struktury dla ABI i zadbała o to, aby dodatkowe pola nie powodowały różnic funkcjonalnych, umożliwiając partnerom uwzględnienie CONFIG_NL80211_TESTMODE w jądrach produkcyjnych przy zachowaniu zgodności z GKI.

Egzekwowanie KMI w czasie wykonywania

Kernele GKI korzystają z opcji konfiguracji TRIM_UNUSED_KSYMS=yUNUSED_KSYMS_WHITELIST=<union of all symbol lists>, które ograniczają eksportowane symbole (takie jak symbole eksportowane za pomocą EXPORT_SYMBOL_GPL()) do tych, które znajdują się na liście symboli. Wszystkie inne symbole nie są eksportowane, a wczytywanie modułu wymagającego niewyeksportowanego symbolu jest odrzucane. To ograniczenie jest egzekwowane w momencie kompilacji, a brakujące wpisy są oznaczane.

Na potrzeby programowania możesz użyć wersji jądra GKI, która nie obejmuje przycinania symboli (co oznacza, że można używać wszystkich symboli wyeksportowanych w standardowy sposób). Aby znaleźć te wersje, poszukaj wersji kernel_debug_aarch64 na stronie ci.android.com.

Egzekwowanie KMI za pomocą wersji modułu

Kernele z obrazu Generic Kernel Image (GKI) korzystają z wersji modułów (CONFIG_MODVERSIONS) jako dodatkowego sposobu zapewnienia zgodności z KMI w czasie wykonywania. Wersja modułu może powodować błędy niezgodności CRC podczas wczytywania modułu, jeśli oczekiwany KMI modułu nie jest zgodny z vmlinux KMI. Oto przykład typowego błędu, który występuje podczas wczytywania modułu z powodu niezgodności CRC dla symbolu 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 ''

Zastosowanie obsługi wersji modułów

Wersje modułów są przydatne z tych powodów:

  • Wersje modułów rejestrują zmiany w widoczności struktury danych. Jeśli moduły zmieniają nieprzezroczyste struktury danych, czyli struktury danych, które nie są częścią KMI, przestają działać po wprowadzeniu zmian w strukturze.

    Weź pod uwagę na przykład pole fwnode w pliku struct device. To pole MUSI być nieprzezroczyste dla modułów, aby nie mogły one wprowadzać zmian w polach device->fw_node ani zakładać jego rozmiaru.

    Jeśli jednak moduł zawiera element <linux/fwnode.h> (bezpośrednio lub pośrednio), pole fwnode w elementzie struct device nie jest już dla niego nieprzezroczyste. Następnie moduł może wprowadzić zmiany w  lub device->fwnode->ops.device->fwnode->dev Ten scenariusz jest problematyczny z kilku powodów:

    • Może to zakłócić założenia, które kod jądra przyjmuje na temat swoich wewnętrznych struktur danych.

    • Jeśli przyszła aktualizacja jądra zmieni struct fwnode_handle (typ danych fwnode), moduł przestanie działać z nowym jądrem. Ponadto stgdiff nie będzie wykazywać żadnych różnic, ponieważ moduł narusza KMI, bezpośrednio manipulując wewnętrznymi strukturami danych w sposób, którego nie można uchwycić, tylko przeglądając reprezentację binarną.

  • Aktualny moduł jest uznawany za niezgodny z KMI, gdy zostanie załadowany w późniejszym terminie przez nowe, niezgodne jądro. Wersja modułu dodaje kontrolę w czasie działania, aby uniknąć przypadkowego załadowania modułu, który nie jest zgodny z jądrem pod kątem KMI. Ten test zapobiega trudnym do debugowania problemom w czasie działania i awariom jądra, które mogą być spowodowane niewykrytą niezgodnością w KMI.

Włączenie wersji modułów zapobiega wszystkim tym problemom.

Sprawdzanie niezgodności CRC bez uruchamiania urządzenia

Pole stgdiff porównuje i zgłasza rozbieżności CRC między rdzeniami oraz inne różnice w ABI.

Ponadto podczas pełnego kompilowania jądra z włączoną opcją CONFIG_MODVERSIONS generowany jest plik Module.symvers w ramach normalnego procesu kompilacji. Ten plik zawiera po jednym wierszu na każdy symbol wyeksportowany przez jądro (vmlinux) i moduły. Każdy wiersz składa się z wartości CRC, nazwy symbolu, przestrzeni nazw symbolu, nazwy vmlinux lub modułu, który eksportuje symbol, oraz typu eksportu (np. EXPORT_SYMBOL lub EXPORT_SYMBOL_GPL).

Możesz porównać pliki Module.symvers z wersji GKI i swojej wersji, aby sprawdzić, czy w symbolach wyeksportowanych przez vmlinux występują różnice CRC. Jeśli wartość CRC dowolnego symbolu wyeksportowanego przez vmlinux i jest inna niż wartość CRC tego symbolu używanego przez jeden z modułów wczytywanych na urządzeniu, moduł nie wczytuje się.

Jeśli nie masz wszystkich artefaktów kompilacji, ale masz pliki vmlinux z jądra GKI i swojego jądra, możesz porównać wartości CRC dla konkretnego symbolu, wykonując to polecenie w obu jądrach i porównując dane wyjściowe:

nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>

Na przykład to polecenie sprawdza wartość CRC dla symbolu module_layout:

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

Rozwiązywanie problemów z niezgodnością CRC

Aby rozwiązać problem z niezgodnością CRC podczas wczytywania modułu, wykonaj te czynności:

  1. Utwórz jądro GKI i jądro urządzenia, używając opcji --kbuild_symtypes, jak pokazano w tym poleceniu:

    tools/bazel run --kbuild_symtypes //common:kernel_aarch64_dist

    To polecenie generuje plik .symtypes dla każdego pliku .o. Więcej informacji znajdziesz w KBUILD_SYMTYPES w Kleaf.

    W przypadku Androida 13 i starszych kompiluj jądro GKI oraz jądro urządzenia, dodając do polecenia kompilacji jądra prefiks KBUILD_SYMTYPES=1, jak w tym przykładzie:

    KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

    Gdy używasz parametru build_abi.sh,, flaga KBUILD_SYMTYPES=1 jest już domyślnie ustawiona.

  2. Znajdź plik .c, w którym wyeksportowano symbol z niezgodem CRC, za pomocą tego polecenia:

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
  3. Plik .c ma odpowiadający mu plik .symtypes w GKI oraz artefakty kompilacji jądra urządzenia. Znajdź plik .c, używając tych poleceń:

    cd out/$BRANCH/common && ls -1 kernel/module.*
    kernel/module.o
    kernel/module.o.symversions
    kernel/module.symtypes

    Poniżej przedstawiono właściwości pliku .c:

    • Format pliku .c to 1 wiersz (potencjalnie bardzo długi) na symbol.

    • [s|u|e|etc]# na początku wiersza oznacza, że symbol ma typ danych [struct|union|enum|etc]. Przykład:

      t#bool typedef _Bool bool
      
    • Brak prefiksu # na początku wiersza wskazuje, że symbol jest funkcją. Przykład:

      find_module s#module * find_module ( const char * )
      
  4. Porównaj oba pliki i usuń wszystkie różnice.

Przypadek 1. Różnice wynikające z widoczności typu danych

Jeśli jedno jądro ukrywa symbol lub typ danych przed modułami, a drugie nie, ta różnica pojawi się w plikach .symtypes tych dwóch jąder. Plik .symtypes z jednego z jąder ma UNKNOWN dla symbolu, a plik .symtypes z drugiego jądra ma rozszerzony widok symbolu lub typu danych.

Na przykład dodanie do pliku include/linux/device.h w jądrze wiersza kodu, który powoduje niezgodności CRC, powoduje niezgodności CRC, z których jedna dotyczy module_layout():

 #include <linux/fwnode.h>

Porównanie module.symtypes dla tego symbolu pokazuje te różnice:

 $ diff -u <GKI>/kernel/module.symtypes <your kernel>/kernel/module.symtypes
  --- <GKI>/kernel/module.symtypes
  +++ <your kernel>/kernel/module.symtypes
  @@ -334,12 +334,15 @@
  ...
  -s#fwnode_handle struct fwnode_handle { UNKNOWN }
  +s#fwnode_reference_args struct fwnode_reference_args { s#fwnode_handle * fwnode ; unsigned int nargs ; t#u64 args [ 8 ] ; }
  ...

Jeśli wartość Twojego jądra to UNKNOWN, a jądro GKI zawiera rozszerzony widok symbolu (co jest bardzo mało prawdopodobne), scal najnowsze jądro Android Common Kernel ze swoim jądrem, aby używać najnowszej wersji jądra GKI.

W większości przypadków wartość rdzenia GKI to UNKNOWN, ale Twój rdzeń zawiera wewnętrzne szczegóły symbolu z powodu zmian wprowadzonych w Twoim rdzeniu. Dzieje się tak, ponieważ jeden z plików w jądrze dodał element #include, którego nie ma w jądrze GKI.

Często wystarczy ukryć nową wartość #include z elementu genksyms.

#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif

Aby zidentyfikować #include, który powoduje różnicę, wykonaj te czynności:

  1. Otwórz plik nagłówka, który definiuje symbol lub typ danych z tą różnicą. Zmodyfikuj na przykład kolumnę include/linux/fwnode.h w wierszu struct fwnode_handle.

  2. U góry pliku nagłówka dodaj ten kod:

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. W pliku .c modułu, który ma niezgodność CRC, dodaj następujący wiersz jako pierwszy przed dowolnym wierszem #include.

    #define CRC_CATCH 1
    
  4. Zkompiluj moduł. Wynikający z tego błąd w czasie kompilacji pokazuje łańcuch plików nagłówka #include, który doprowadził do niezgodności CRC. Przykład:

    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"
    

    Jeden z linków w tym łańcuchu #include jest spowodowany zmianą w jądrze, która nie występuje w jądrze GKI.

  5. Zidentyfikuj zmianę, cofnij ją w jądrze lub prześlij do ACK i połącz ją z innymi zmianami.

Przypadek 2. Różnice spowodowane zmianami typu danych

Jeśli rozbieżność CRC symbolu lub typu danych nie jest spowodowana różnicą w widoczności, to wynika to z rzeczywistych zmian (dodanych, usuniętych lub zmienionych) w samym typie danych.

Na przykład wprowadzenie tej zmiany w jądrze powoduje kilka niezgodności CRC, ponieważ wiele symboli jest pośrednio dotkniętych tego typu zmianą:

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);

Jeden niespójny CRC dotyczy devm_of_platform_populate().

Jeśli porównasz pliki .symtypes dla tego symbolu, mogą one wyglądać tak:

 $ diff -u <GKI>/drivers/of/platform.symtypes <your kernel>/drivers/of/platform.symtypes
  --- <GKI>/drivers/of/platform.symtypes
  +++ <your kernel>/drivers/of/platform.symtypes
  @@ -399,7 +399,7 @@
  ...
  -s#iommu_ops struct iommu_ops { ... ; t#phy
  s_addr_t ( * iova_to_phys_hard ) ( s#iommu_domain * , t#dma_addr_t ) ; int
    ( * add_device ) ( s#device * ) ; ...
  +s#iommu_ops struct iommu_ops { ... ; t#phy
  s_addr_t ( * iova_to_phys_hard ) ( s#iommu_domain * , t#dma_addr_t , unsigned long ) ; int ( * add_device ) ( s#device * ) ; ...

Aby określić zmieniony typ, wykonaj te czynności:

  1. Znajdź definicję symbolu w kodzie źródłowym (zwykle w plikach .h).

    • Aby znaleźć różnice w symbolach między Twoim jądrem a jądrem GKI, znajdź zatwierdzanie, uruchamiając to polecenie:
    git blame
    • W przypadku usuniętych symboli (gdy symbol został usunięty z drzewa i chcesz go usunąć z drugiego drzewa) musisz znaleźć zmianę, która spowodowała usunięcie linii. Użyj tego polecenia w drzewie, w którym usunięto linię:
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
  2. Przejrzyj zwróconą listę zatwierdzeń, aby znaleźć zmianę lub usunięcie. Pierwszy commit to prawdopodobnie ten, którego szukasz. Jeśli nie, przejrzyj listę, aż znajdziesz odpowiednią wersję.

  3. Po zidentyfikowaniu zmiany cofnij ją w jądrze lub prześlij do ACK i połącz ją z jądrem.