Monitorowanie ABI jądra Androida

Możesz użyć narzędzi do monitorowania interfejsu binarnego (ABI), dostępnych w systemie Android 11 i nowszych, aby ustabilizować wbudowany interfejs ABI jąder systemu Android. Narzędzie zbiera i porównuje reprezentacje ABI z istniejących plików binarnych jądra (moduły vmlinux + GKI). Te reprezentacje ABI to pliki .stg i listy symboli. Interfejs, na którym reprezentacja udostępnia widok, nazywany jest interfejsem modułu jądra (KMI). Możesz użyć tego narzędzia do śledzenia i łagodzenia zmian w KMI.

Narzędzia do monitorowania ABI zostały opracowane w AOSP i wykorzystują STG (lub libabigail w systemie Android 13 i starszych) do generowania i porównywania reprezentacji.

Na tej stronie opisano narzędzia, proces gromadzenia i analizowania reprezentacji ABI oraz wykorzystanie takich reprezentacji w celu zapewnienia stabilności wbudowanego ABI. Na tej stronie znajdują się także informacje dotyczące wprowadzania zmian w jądrach systemu Android.

Proces

Analiza ABI jądra wymaga wielu kroków, z których większość można zautomatyzować:

  1. Zbuduj jądro i jego reprezentację ABI .
  2. Przeanalizuj różnice ABI między kompilacją a referencją .
  3. Zaktualizuj reprezentację ABI (jeśli jest to wymagane) .
  4. Pracuj z listami symboli .

Poniższe instrukcje działają w przypadku każdego jądra, które można zbudować przy użyciu obsługiwanego zestawu narzędzi (takiego jak wstępnie zbudowany zestaw narzędzi Clang). repo manifests są dostępne dla wszystkich popularnych gałęzi jądra Androida, a w przypadku kilku jąder specyficznych dla urządzenia zapewniają, że podczas tworzenia dystrybucji jądra do analizy zostanie użyty poprawny zestaw narzędzi.

Listy symboli

KMI nie zawiera wszystkich symboli w jądrze ani nawet wszystkich ponad 30 000 wyeksportowanych symboli. Zamiast tego symbole, których mogą używać moduły dostawcy, są wyraźnie wymienione w zestawie plików list symboli utrzymywanych publicznie w katalogu głównym drzewa jądra. Suma wszystkich symboli we wszystkich plikach list symboli definiuje zbiór symboli KMI utrzymywanych jako stabilny. Przykładowym plikiem listy symboli jest abi_gki_aarch64_db845c , który deklaruje symbole wymagane dla DragonBoard 845c .

Tylko symbole wymienione na liście symboli oraz powiązane z nimi struktury i definicje są uważane za część KMI. Możesz opublikować zmiany na swoich listach symboli, jeśli nie ma potrzebnych symboli. Gdy nowe interfejsy znajdą się na liście symboli i staną się częścią opisu KMI, są utrzymywane jako stabilne i nie wolno ich usuwać z listy symboli ani modyfikować po zamrożeniu gałęzi.

Każda gałąź jądra KMI Android Common Kernel (ACK) ma swój własny zestaw list symboli. Nie podejmuje się żadnych prób zapewnienia stabilności ABI pomię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ć, które interfejsy muszą być monitorowane pod kątem stabilności. Główna lista symboli zawiera symbole wymagane przez moduły jądra GKI. Oczekuje się, że dostawcy będą przesyłać i aktualizować dodatkowe listy symboli, aby zapewnić, że interfejsy, na których polegają, zachowują zgodność z ABI. Na przykład, aby zobaczyć listę list symboli dla android13-5.15 , zobacz https://android.googlesource.com/kernel/common/+/refs/heads/android13-5.15/android

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

Po zamrożeniu KMI nie są dozwolone żadne zmiany w istniejących interfejsach KMI; są stabilne. Jednak dostawcy mogą w dowolnym momencie dodawać symbole do KMI, o ile dodatki nie wpływają na stabilność istniejącego ABI. Nowo dodane symbole pozostają stabilne, gdy tylko zostaną cytowane na liście symboli KMI. Symbole nie powinny być usuwane z listy jądra, chyba że można potwierdzić, że żadne urządzenie nigdy nie było zależne od tego symbolu.

Możesz wygenerować listę symboli KMI dla urządzenia, korzystając z instrukcji z Jak pracować z listami symboli . Wielu partnerów przesyła jedną listę symboli na każde potwierdzenie ACK, ale nie jest to trudny wymóg. Jeśli pomoże to w utrzymaniu, możesz przesłać wiele list symboli.

Rozszerz KMI

Chociaż symbole KMI i powiązane struktury są utrzymywane jako stabilne (co oznacza, że ​​nie można zaakceptować zmian, które psują stabilne interfejsy w jądrze z zamrożonym KMI), jądro GKI pozostaje otwarte na rozszerzenia, dzięki czemu urządzenia dostarczane później w tym roku nie muszą definiować wszystkich ich zależności przed zamrożeniem KMI. Aby rozszerzyć KMI, możesz dodać do KMI 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ż zaakceptowane, jeśli nie psują KMI.

O awariach KMI

Jądro ma źródła i pliki binarne są budowane z tych źródeł. Gałęzie jądra monitorowane przez ABI zawierają reprezentację ABI bieżącego ABI GKI (w postaci pliku .stg ). Po zbudowaniu plików binarnych ( vmlinux , Image i dowolne moduły GKI) z plików binarnych można wyodrębnić reprezentację ABI. Wszelkie zmiany wprowadzone w pliku źródłowym jądra mogą mieć wpływ na pliki binarne, a co za tym idzie, również na wyodrębniony .stg . Analizator AbiAnalyzer porównuje zatwierdzony plik .stg z plikiem wyodrębnionym z artefaktów kompilacji i ustawia etykietę Lint-1 dla zmiany w Gerrit, jeśli znajdzie różnicę semantyczną.

Radź sobie z awariami ABI

Jako przykład, poniższa łatka wprowadza bardzo oczywiste uszkodzenie 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.

Po uruchomieniu kompilacji ABI z zastosowaną tą łatką narzędzie kończy działanie z niezerowym kodem błędu i zgłasza różnicę 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 ABI wykryte w czasie kompilacji

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

Jeśli symbolu nie ma na liście symboli ( android/abi_gki_aarch64 ), musisz najpierw sprawdzić, czy został wyeksportowany za pomocą EXPORT_SYMBOL_GPL( symbol_name ) , a następnie zaktualizować reprezentację ABI XML i listę symboli. Na przykład następujące zmiany dodają nową funkcję przyrostowego FS do gałęzi android-12-5.10 , która obejmuje aktualizację listy symboli i reprezentacji ABI XML.

Jeśli symbol został wyeksportowany (przez Ciebie lub został wcześniej wyeksportowany), ale żaden inny sterownik go nie używa, może pojawić się błąd kompilacji podobny do poniższego.

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 potwierdzeniu ACK (patrz Aktualizacja reprezentacji ABI ). Przykład aktualizacji XML ABI i listy symboli w potwierdzeniu ACK można znaleźć w aosp/1367601 .

Napraw awarie ABI jądra

Z awariami ABI jądra można sobie poradzić, refaktoryzując kod tak, aby nie zmieniać ABI lub aktualizując reprezentację ABI . Skorzystaj z poniższej tabeli, aby określić najlepsze podejście w swojej sytuacji.

Schemat blokowania ABI

Rysunek 1. Rozwiązanie problemu awarii ABI

Refaktoryzuj kod, aby uniknąć zmian ABI

Dołóż wszelkich starań, aby uniknąć modyfikacji istniejącego ABI. W wielu przypadkach możesz dokonać refaktoryzacji kodu, aby usunąć zmiany wpływające na ABI.

  • Refaktoryzacja zmian w polu struktury. Jeśli zmiana modyfikuje ABI dla funkcji debugowania, dodaj #ifdef wokół pól (w strukturach i odniesieniach źródłowych) i upewnij się, że CONFIG używany dla #ifdef jest wyłączony dla produkcyjnych defconfig i gki_defconfig . Przykład tego, jak można dodać konfigurację debugowania do struktury bez psucia ABI, można znaleźć w tym zestawie poprawek .

  • Funkcje refaktoryzacji, aby nie zmieniać jądra rdzenia. Jeśli trzeba dodać nowe funkcje do ACK w celu obsługi modułów partnerskich, spróbuj refaktoryzować część zmiany ABI, aby uniknąć modyfikowania ABI jądra. Przykład wykorzystania istniejącego ABI jądra do dodania dodatkowej funkcjonalności bez zmiany ABI jądra można znaleźć w aosp/1312213 .

Napraw uszkodzony ABI na Androidzie Gerrit

Jeśli nie uszkodziłeś celowo ABI jądra, musisz to zbadać, korzystając ze wskazówek dostarczonych przez narzędzia monitorujące ABI. Najczęstszymi przyczynami awarii są zmienione struktury danych i powiązane zmiany CRC symboli lub zmiany opcji konfiguracyjnych, które prowadzą do któregokolwiek z wyżej wymienionych. Rozpocznij od rozwiązania problemów wykrytych przez narzędzie.

Możesz odtworzyć wyniki ABI lokalnie, zobacz Tworzenie jądra i jego reprezentacja ABI .

Informacje o etykietach Lint-1

Jeśli przesyłasz zmiany do gałęzi zawierającej zamrożony lub sfinalizowany KMI, zmiany muszą przejść przez AbiAnalyzer , aby mieć pewność, że zmiany nie wpłyną na stabilny ABI w niekompatybilny sposób. Podczas tego procesu AbiAnalyzer szuka raportu ABI utworzonego podczas kompilacji (rozszerzona kompilacja, która wykonuje normalną kompilację, a następnie pewne kroki wyodrębnienia i porównania ABI.

Jeśli AbiAnalyzer znajdzie niepusty raport, ustawia etykietę Lint-1, a przesłanie zmiany jest blokowane do czasu rozwiązania; dopóki zestaw poprawek nie otrzyma etykiety Lint+1.

Zaktualizuj ABI jądra

Jeśli modyfikacja ABI jest nieunikniona, musisz zastosować zmiany w kodzie, reprezentację ABI i listę symboli w potwierdzeniu ACK. Aby Lint usunął -1 i nie złamał kompatybilności z GKI, wykonaj następujące kroki:

  1. Prześlij zmiany kodu do pliku ACK .

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

  3. Zaktualizuj referencyjną reprezentację ABI .

  4. Połącz zmiany w kodzie i zmianę aktualizacji ABI.

Prześlij zmiany w kodzie ABI do pliku ACK

Aktualizacja ACK ABI zależy od rodzaju wprowadzanej zmiany.

  • Jeśli zmiana ABI jest związana z funkcją wpływającą na testy CTS lub VTS, zmianę można zwykle wybrać jako potwierdzenie w niezmienionej postaci. Na przykład:

  • Jeśli zmiana ABI dotyczy funkcji, którą można udostępnić w potwierdzeniu ACK, zmiana ta może zostać wybrana jako ACK w niezmienionej postaci. Na przykład następujące zmiany nie są potrzebne do testu CTS ani VTS, ale można je udostępnić za pomocą ACK:

  • Jeśli zmiana ABI wprowadza nową funkcję, która nie musi być uwzględniana w potwierdzeniu ACK, możesz wprowadzić symbole do potwierdzenia za pomocą kodu pośredniczącego, jak opisano w poniższej sekcji.

Użyj kodu pośredniczącego dla ACK

Kody pośredniczące muszą być konieczne tylko w przypadku podstawowych zmian jądra, które nie przynoszą korzyści ACK, takich jak zmiany wydajności i zasilania. Na poniższej liście znajdują się szczegółowe przykłady fragmentów i częściowych picków w potwierdzeniu ACK dla GKI.

  • Odgałęzienie funkcji izolowania rdzenia ( aosp/1284493 ). Funkcjonalność ACK nie jest konieczna, ale symbole muszą być obecne w ACK, aby moduły mogły z nich korzystać.

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

  • Tylko ABI wybór funkcji śledzenia zdarzeń mm dla każdego procesu ( aosp/1288454 ). Oryginalna łatka została wybrana do ACK, a następnie przycięta, aby zawierała tylko niezbędne zmiany, aby rozwiązać różnicę ABI dla task_struct i mm_event_count . Ta poprawka aktualizuje również wyliczenie mm_event_type , tak aby zawierało ostateczne elementy członkowskie.

  • Częściowy wybór zmian ABI w strukturze termicznej, które wymagały czegoś więcej niż tylko dodania nowych pól ABI.

    • Łatka aosp/1255544 rozwiązała różnice w ABI pomiędzy jądrem partnera a potwierdzeniem ACK.

    • Łatka aosp/1291018 naprawiła problemy funkcjonalne znalezione podczas testowania poprzedniej łatki przez GKI. Poprawka obejmowała inicjalizację struktury parametrów czujnika w celu zarejestrowania wielu stref termicznych w jednym czujniku.

  • Zmiany w CONFIG_NL80211_TESTMODE ABI ( aosp/1344321 ). Ta poprawka dodała niezbędne zmiany w strukturze ABI i upewniła się, że dodatkowe pola nie powodują różnic funkcjonalnych, umożliwiając partnerom włączenie CONFIG_NL80211_TESTMODE do swoich jąder produkcyjnych i nadal zachowują zgodność z GKI.

Wymuś KMI w czasie wykonywania

Jądra GKI używają opcji konfiguracyjnych 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 ładowanie modułu wymagającego niewyeksportowanego symbolu jest zabronione. To ograniczenie jest egzekwowane w czasie kompilacji, a brakujące wpisy są oznaczane.

Do celów programistycznych można użyć kompilacji jądra GKI, która nie obejmuje przycinania symboli (co oznacza, że ​​można używać wszystkich zwykle eksportowanych symboli). Aby zlokalizować te kompilacje, poszukaj kompilacji kernel_debug_aarch64 na ci.android.com .

Wymuś KMI przy użyciu wersjonowania modułów

Jądra ogólnego obrazu jądra (GKI) wykorzystują wersjonowanie modułów ( CONFIG_MODVERSIONS ) jako dodatkowy środek zapewniający zgodność z KMI w czasie wykonywania. Wersjonowanie modułów może powodować błędy niedopasowania cyklicznej kontroli nadmiarowej (CRC) w czasie ładowania modułu, jeśli oczekiwany KMI modułu nie pasuje do KMI vmlinux . Na przykład typowy błąd występujący w czasie ładowania modułu z powodu niedopasowania 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 ''

Zastosowania wersjonowania modułów

Wersjonowanie modułów jest przydatne z następujących powodów:

  • Wersjonowanie modułów wychwytuje zmiany w widoczności struktury danych. Jeśli moduły zmieniają nieprzezroczyste struktury danych, to znaczy struktury danych, które nie są częścią KMI, psują się po przyszłych zmianach w strukturze.

    Jako przykład rozważmy pole fwnode w struct device . To pole MUSI być nieprzezroczyste dla modułów, aby nie mogły wprowadzać zmian w polach device->fw_node ani przyjmować założeń co do jego rozmiaru.

    Jeśli jednak moduł zawiera <linux/fwnode.h> (bezpośrednio lub pośrednio), wówczas pole fwnode w struct device nie jest już dla niego nieprzezroczyste. Moduł może następnie wprowadzić zmiany w device->fwnode->dev lub device->fwnode->ops . Scenariusz ten jest problematyczny z kilku powodów, które przedstawiono poniżej:

    • Może złamać założenia, jakie główny 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. Co więcej, stgdiff nie pokaże żadnych różnic, ponieważ moduł łamie KMI, bezpośrednio manipulując wewnętrznymi strukturami danych w sposób, którego nie można uchwycić jedynie poprzez sprawdzenie reprezentacji binarnej.

  • Bieżący moduł jest uznawany za niezgodny z KMI, jeśli zostanie później załadowany przez nowe jądro, które jest niekompatybilne. Wersjonowanie modułów dodaje kontrolę w czasie wykonywania, aby uniknąć przypadkowego załadowania modułu, który nie jest zgodny z KMI z jądrem. To sprawdzenie zapobiega trudnym do debugowania problemom ze środowiskiem wykonawczym i awariom jądra, które mogą wynikać z niewykrytej niezgodności w KMI.

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

Sprawdź niezgodność CRC bez uruchamiania urządzenia

stgdiff porównuje i raportuje niedopasowania CRC między jądrami wraz z innymi różnicami ABI.

Ponadto pełna kompilacja jądra z włączoną opcją CONFIG_MODVERSIONS generuje plik Module.symvers w ramach normalnego procesu kompilacji. Ten plik ma jedną linię dla każdego symbolu wyeksportowanego przez jądro ( vmlinux ) i moduły. Każda linia składa się z wartości CRC, nazwy symbolu, przestrzeni nazw symboli, nazwy vmlinux lub modułu eksportującego symbol oraz typu eksportu (na przykład EXPORT_SYMBOL kontra EXPORT_SYMBOL_GPL ).

Możesz porównać pliki Module.symvers pomiędzy kompilacją GKI a twoją kompilacją, aby sprawdzić różnice CRC w symbolach eksportowanych przez vmlinux . Jeśli istnieje różnica wartości CRC w jakimkolwiek symbolu wyeksportowanym przez vmlinux i ten symbol jest używany przez jeden z modułów, które ładujesz do swojego urządzenia, moduł nie zostanie załadowany.

Jeśli nie masz wszystkich artefaktów kompilacji, ale masz pliki vmlinux jądra GKI i swojego jądra, możesz porównać wartości CRC dla określonego symbolu, uruchamiając następującą komendę na obu jądrach i porównując dane wyjściowe:

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

Na przykład poniższe polecenie sprawdza wartość CRC dla symbolu module_layout :

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

Rozwiąż niedopasowania CRC

Wykonaj poniższe kroki, aby rozwiązać problem niezgodności CRC podczas ładowania modułu:

  1. Zbuduj jądro GKI i jądro swojego urządzenia, używając opcji --kbuild_symtypes , jak pokazano w następującym poleceniu:

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

    To polecenie generuje plik .symtypes dla każdego pliku .o . Aby uzyskać szczegółowe informacje, zobacz KBUILD_SYMTYPES w Kleaf .

    W przypadku Androida 13 i starszych zbuduj jądro GKI i jądro swojego urządzenia, dodając KBUILD_SYMTYPES=1 do polecenia używanego do budowania jądra, jak pokazano w następującym poleceniu:

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

    Podczas korzystania build_abi.sh, flaga KBUILD_SYMTYPES=1 jest już domyślnie ustawiona.

  2. Znajdź plik .c , do którego eksportowany jest symbol z niezgodnością CRC, używając następującego polecenia:

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
    
  3. Plikowi .c odpowiada plik .symtypes w GKI oraz artefakty kompilacji jądra urządzenia. Zlokalizuj plik .c za pomocą następujących poleceń:

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

    Poniżej przedstawiono cechy pliku .c :

    • Format pliku .c to jedna (potencjalnie bardzo długa) linia na symbol.

    • [s|u|e|etc]# na początku linii oznacza, że ​​symbol jest typu danych [struct|union|enum|etc] . Na przykład:

      t#bool typedef _Bool bool
      
    • Brakujący przedrostek # na początku linii wskazuje, że symbol jest funkcją. Na przykład:

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

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

Jeśli jedno jądro utrzymuje symbol lub typ danych niewidoczny dla modułów, a drugie jądro tego nie robi, różnica pojawia się pomiędzy plikami .symtypes obu jąder. Plik .symtypes z jednego z jąder ma symbol UNKNOWN , a plik .symtypes z drugiego jądra ma rozszerzony widok symbolu lub typu danych.

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

 #include <linux/fwnode.h>

Porównanie module.symtypes dla tego symbolu ujawnia następujące 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 twoje jądro ma wartość UNKNOWN , a jądro GKI ma rozszerzony widok symbolu (bardzo mało prawdopodobne), połącz najnowsze wspólne jądro systemu Android ze swoim jądrem, aby używać najnowszej podstawy jądra GKI.

W większości przypadków jądro GKI ma wartość UNKNOWN , ale twoje jądro ma wewnętrzne szczegóły symbolu ze względu na zmiany wprowadzone w twoim jądrze. Dzieje się tak, ponieważ jeden z plików w twoim jądrze dodał #include , którego nie ma w jądrze GKI.

Często rozwiązanie jest tak proste, jak ukrycie nowego #include przed genksyms .

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

W przeciwnym razie, aby zidentyfikować #include powodujący różnicę, wykonaj następujące kroki:

  1. Otwórz plik nagłówkowy, który definiuje symbol lub typ danych charakteryzujący się tą różnicą. Na przykład edytuj include/linux/fwnode.h dla struct fwnode_handle .

  2. Dodaj następujący kod na górze pliku nagłówkowego:

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

    #define CRC_CATCH 1
    
  4. Skompiluj moduł. Wynikowy błąd czasu kompilacji pokazuje łańcuch pliku nagłówkowego #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"
    

    Jedno z łączy w tym łańcuchu #include wynika ze zmiany dokonanej w jądrze, której brakuje w jądrze GKI.

  5. Zidentyfikuj zmianę, przywróć ją w jądrze lub prześlij do ACK i połącz .

Przypadek 2: Różnice spowodowane zmianami typu danych

Jeśli niezgodność CRC dla symbolu lub typu danych nie jest spowodowana różnicą w widoczności, jest to spowodowane rzeczywistymi zmianami (dodatkami, usunięciami lub zmianami) w samym typie danych.

Na przykład dokonanie następującej zmiany w jądrze powoduje kilka niezgodności CRC, ponieważ tego typu zmiany pośrednio wpływają na wiele symboli:

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() .

Jeśli porównasz pliki .symtypes dla tego symbolu, może to 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 zidentyfikować zmieniony typ, wykonaj następujące kroki:

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

    • Aby poznać proste różnice w symbolach pomiędzy jądrem a jądrem GKI, znajdź zatwierdzenie, uruchamiając następującą komendę:
    git blame
    
    • W przypadku usuniętych symboli (gdzie symbol został usunięty w drzewie i chcesz go również usunąć w innym drzewie), musisz znaleźć zmianę, która spowodowała usunięcie linii. Użyj następującego polecenia na drzewie, z którego usunięto linię:
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
    
  2. Przejrzyj zwróconą listę zatwierdzeń, aby zlokalizować zmianę lub usunięcie. Pierwsze zatwierdzenie jest prawdopodobnie tym, którego szukasz. Jeśli tak nie jest, przejrzyj listę, aż znajdziesz zatwierdzenie.

  3. Po zidentyfikowaniu zmiany przywróć ją w jądrze lub prześlij do ACK i połącz .