Monitorowanie interfejsu ABI jądra Androida

Możesz użyć narzędzi do monitorowania interfejsu binarnego aplikacji (ABI) dostępnych w Androidzie 11 i nowszych, aby ustabilizować interfejs ABI działający w jądrze Androida. Narzędzie zbiera i porównuje reprezentacje ABI z dotychczasowych binarnych wersji jądra (vmlinux + moduły GKI). Te reprezentacje interfejsu 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 opisujemy narzędzia, proces zbierania i analizowania reprezentacji ABI oraz używanie 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 kroków, z których większość można zautomatyzować:

  1. Utwórz jądro i jego reprezentacja ABI.
  2. Przeanalizuj różnice w interfejsie 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żna skompilować za pomocą obsługiwanego zestawu narzędzi (np. wstępnie skompilowanego zestawu narzędzi Clang). repo manifests są dostępne w przypadku wszystkich popularnych gałęzi jądra Androida, a w przypadku kilku jądra różnych urządzeń dają pewność, że podczas tworzenia dystrybucji jądra do analizy używany jest właściwy łańcuch narzędzi.

Listy symboli

KMI nie zawiera wszystkich symboli w jądrze,a nawet wszystkich ponad 30 000 wyeksportowanych symboli. Zamiast tego symbole, których mogą używać moduły dostawców, są wymienione w zestawie plików listy symboli, które są publicznie dostępne w katalogu głównym drzewa jądra. 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 abi_gki_aarch64_db845c, który deklaruje symbole wymagane dla 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. Nowe interfejsy umieszczone na liście symboli i należące do opisu KMI są zachowywane jako stabilne i nie można ich usuwać z listy symboli ani modyfikować po zawieszeniu 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. Lista głównych symboli zawiera symbole wymagane przez moduły jądra GKI. Dostawcy powinni przesyłać i aktualizować dodatkowe listy symboli, aby mieć pewność, że interfejsy, których używają, zachowują zgodność z ABI. Aby na przykład wyświetlić listę list symboli dla android13-5.15, użyj kodu https://android.googlesource.com/kernel/common/+/refs/heads/android13-5.15/android.

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 na najbardziej stabilnym poziomie, gdy tylko zostaną cytowane 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 ACK, 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

Jądro ma źródła i na ich podstawie są tworzone pliki binarne. Gałęzie jądra monitorowane przez ABI zawierają reprezentację bieżącego interfejsu GKI ABI przez ABI (w postaci pliku .stg). Po skompilowaniu plików binarnych (vmlinux, Image i dowolnych 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.

Obsługa awarii 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żywanie przez sterownika nowego symbolu z jądra, którego nie ma na żadnej liście symboli.

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

Jeśli symbol jest wyeksportowany (przez Ciebie lub wcześniej), ale nie jest używany przez żaden inny sterownik, 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 pliku XML ABI i listy symboli w pliku ACK znajdziesz w problemie aosp/1367601.

Napraw błędy ABI jądra

Aby rozwiązać problemy z awariami interfejsu ABI jądra, możesz zrefaktoryzować kod, aby nie zmieniać interfejsu ABI, lub zaktualizować reprezentację interfejsu ABI. Na podstawie poniższej tabeli określ, która metoda jest najlepsza w Twojej sytuacji.

Schemat procesu w przypadku naruszenia interfejsu ABI

Rysunek 1. Rozwiązanie problemu z naruszeniem ABI

Refaktoryzacja kodu w celu uniknięcia zmian w interfejsie 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.

  • Zmiany w polu struktury refaktoryzacyjnej. 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.

  • Funkcje refaktoryzacyjne pozwalające nie zmieniać podstawowego jądra. Jeśli do obsługi modułów partnera trzeba dodać do ACK nowych funkcji, 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 Gerrtie 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ęstszą przyczyną awarii są zmiany struktury danych i powiązanych z nimi zmian CRC symboli lub zmiany opcji konfiguracji, które prowadzą do wymienionych powyżej problemów. Zacznij od rozwiązania problemów wykrytych przez narzędzie.

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

Etykiety Lint-1

Jeśli przesyłasz zmiany do gałęzi zawierającej zamrożoną lub sfinalizowaną KMI, zmiany muszą przejść weryfikację AbiAnalyzer, aby upewnić się, że nie wpływają 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 wyodrębniania i porównywania ABI).

Jeśli AbiAnalyzer znajdzie niepusty raport, ustawi etykietę Lint-1, a zmianę zablokuje do momentu jej rozwiązania, czyli dopóki zestaw poprawek nie otrzyma etykiety 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. Prześlij zmiany w kodzie do potwierdzenia.

  2. Zaczekaj 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ą przenieść do tej wersji w postaci dosłownej. Na przykład te zmiany nie są potrzebne do testu CTS ani VTS, ale można je udostępnić w wersji ACK:

  • Jeśli zmiana w interfejsie ABI wprowadza nową funkcję, której nie trzeba uwzględniać w potwierdzeniu, możesz wprowadzić symbole do potwierdzenia za pomocą fragmentu w sposób opisany w następnej sekcji.

Użyj wycinków z kodem ACK

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 przykłady wycinków i częściowych wycinków w kodzie ACK dla GKI.

  • Element podrzędny funkcji izolowania rdzenia (aosp/1284493). Funkcje w ACK nie są wymagane, ale symbole muszą być obecne w ACK, aby moduły mogły ich używać.

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

  • Funkcja śledzenia zdarzeń mm (aosp/1288454) korzystająca z funkcji selektywnego mm (tylko ABI). Pierwotny pakiet poprawek został wybrany do zaakceptowania, a potem 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.

  • częściowe 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 interfejsie ABI między jądrem partnera a Potwierdzeniem.

    • Aktualizacja aosp/1291018 naprawia problemy z funkcjonalnością, które zostały wykryte podczas testowania poprzedniej aktualizacji przez GKI. Poprawka 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 dodała niezbędne zmiany struktury dla interfejsu ABI i zadbała o to, aby dodatkowe pola nie zakłócały działania. Dzięki temu partnerzy mogli uwzględnić interfejs CONFIG_NL80211_TESTMODE w jądrze produkcyjnym i zachować zgodność z GKI.

Wymuś KMI w czasie wykonywania

Kernele GKI używają opcji konfiguracji TRIM_UNUSED_KSYMS=y i UNUSED_KSYMS_WHITELIST=<union of all symbol lists>, które ograniczają eksportowane symbole (takie jak symbole eksportowane za pomocą EXPORT_SYMBOL_GPL()) do tych wymienionych 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ć kompilacji jądra GKI, która nie obejmuje przycinania symboli (co oznacza, że można użyć wszystkich zwykle eksportowanych symboli). Aby znaleźć te kompilacje, poszukaj kompilacji 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. Na przykład poniższy błąd występuje podczas ładowania modułu z powodu niezgodności CRC z symbolem 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

Obsługa wersji modułu jest przydatna z tych powodów:

  • Obsługa wersji modułu wychwytuje zmiany w widoczności struktury danych. Jeśli moduły zmieniają nieprzezroczyste struktury danych, czyli struktury danych, które nie są częścią interfejsu 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. Moduł może wtedy wprowadzić zmiany w elemencie device->fwnode->dev lub device->fwnode->ops. 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 reprezentacje binarne.

  • Bieżący moduł jest uznawany za niezgodny z KMI, gdy zostanie wczytany później przez nowe jądro, które będzie niezgodne z zasadami. Obsługa wersji modułów dodaje kontrolę w czasie działania, aby zapobiec przypadkowemu wczytaniu modułu, który nie jest zgodny z jądrem KMI. Ten test zapobiega trudnym do debugowania problemom w czasie działania i awarjom jądra, które mogą być spowodowane niezauważoną 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 zawiera wartość CRC, nazwę symbolu, przestrzeń nazw symbolu, vmlinux lub nazwę modułu, które eksportują symbol, oraz typ eksportu (np. EXPORT_SYMBOL w porównaniu z 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, a symbol jest używany 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 następujące 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 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 symbol z niezgodnością CRC został wyeksportowany, za pomocą tego polecenia:

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
    
  3. Plik .c ma odpowiedni plik .symtypes w GKI i artefakty kompilacji jądra urządzenia. Znajdź plik .c za pomocą tych poleceń:

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

    Poniżej znajdziesz informacje o 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]. Na przykład:

      t#bool typedef _Bool bool
      
    • Brakujący na początku wiersza prefiks # wskazuje, że symbol jest funkcją. Na 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 tego wiersza 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 (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 jądro GKI ma wartość UNKNOWN, ale ze względu na zmiany wprowadzone w jądrze jego wewnętrzne szczegóły zawierają wewnętrzne szczegóły symbolu. Dzieje się tak, ponieważ jeden z plików w jądrze dodał #include, którego nie ma w jądrze GKI.

Często wystarczy ukryć nowy element #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ł. Powstały błąd czasu kompilacji wskazuje łańcuch pliku nagłówka #include, który doprowadził do niezgodności CRC. Na 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 wynikające ze zmian 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);

Jedna niezgodność CRC dotyczy devm_of_platform_populate().

Porównując pliki .symtypes dotyczące tego symbolu, może ono 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 rozpoznaniu zmiany przywróć ją w jądrze lub prześlij do potwierdzenia i poproś o jej scalenie.