Мониторинг ABI ядра Android

Вы можете использовать инструменты мониторинга двоичного интерфейса приложений (ABI), доступные в Android 11 и более поздних версиях, для стабилизации встроенного ABI ядер Android. Инструментарий собирает и сравнивает представления ABI из существующих двоичных файлов ядра (модули vmlinux + GKI). Этими представлениями ABI являются файлы .stg и списки символов. Интерфейс, на котором представление дает представление, называется интерфейсом модуля ядра (KMI). Вы можете использовать этот инструмент для отслеживания и устранения изменений в KMI.

Инструменты мониторинга ABI разработаны в AOSP и используют STG (или libabigail в Android 13 и более ранних версиях) для создания и сравнения представлений.

На этой странице описываются инструменты, процесс сбора и анализа представлений ABI, а также использование таких представлений для обеспечения стабильности встроенного ABI. На этой странице также представлена ​​информация о внесении изменений в ядра Android.

Процесс

Анализ ABI ядра состоит из нескольких шагов, большинство из которых можно автоматизировать:

  1. Создайте ядро ​​и его ABI-представление .
  2. Анализ различий ABI между сборкой и эталоном .
  3. Обновите представление ABI (при необходимости) .
  4. Работа со списками символов .

Следующие инструкции подходят для любого ядра, которое вы можете собрать с помощью поддерживаемой цепочки инструментов (например, предварительно созданной цепочки инструментов Clang). repo manifests доступны для всех общих ветвей ядра Android и для нескольких ядер для конкретных устройств. Они гарантируют, что при сборке дистрибутива ядра для анализа используется правильная цепочка инструментов.

Списки символов

KMI не включает в себя все символы ядра и даже не все из более чем 30 000 экспортированных символов. Вместо этого символы, которые могут использоваться модулями поставщика, явно перечислены в наборе файлов списков символов, которые общедоступны в дереве ядра ( gki/{ARCH}/symbols/* или android/abi_gki_{ARCH}_* в Android 15 и более ранних версиях). Объединение всех символов во всех файлах списка символов определяет набор символов KMI, который поддерживается как стабильный. Пример файла списка символов — gki/aarch64/symbols/db845c , в котором объявляются символы, необходимые для DragonBoard 845c .

Только символы, перечисленные в списке символов, а также связанные с ними структуры и определения считаются частью KMI. Вы можете опубликовать изменения в списках символов, если нужные вам символы отсутствуют. После того как новые интерфейсы попадают в список символов и являются частью описания KMI, они сохраняются как стабильные и их нельзя удалять из списка символов или изменять после замораживания ветки.

Каждая ветвь ядра Android Common Kernel (ACK) KMI имеет свой собственный набор списков символов. Не предпринимается никаких попыток обеспечить стабильность ABI между различными ветвями ядра KMI. Например, KMI для android12-5.10 полностью независим от KMI для android13-5.10 .

Инструменты ABI используют списки символов KMI, чтобы ограничить, какие интерфейсы необходимо отслеживать на предмет стабильности. Ожидается, что поставщики будут предоставлять и обновлять свои собственные списки символов, чтобы гарантировать, что интерфейсы, на которые они полагаются, поддерживают совместимость с ABI. Например, чтобы просмотреть список списков символов для ядра android16-6.12 , обратитесь к https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols .

Список символов содержит символы, которые, как сообщается, необходимы для конкретного поставщика или устройства. Полный список, используемый инструментами, представляет собой объединение всех файлов списков символов KMI. Инструменты ABI определяют детали каждого символа, включая сигнатуру функции и вложенные структуры данных.

Когда KMI заморожен, никакие изменения в существующих интерфейсах KMI не допускаются; они стабильны. Однако поставщики могут добавлять символы в KMI в любое время, если это не влияет на стабильность существующего ABI. Вновь добавленные символы остаются стабильными, как только они упоминаются в списке символов KMI. Символы не следует удалять из списка ядра, пока не будет подтверждено, что ни одно устройство никогда не поставлялось с зависимостью от этого символа.

Вы можете создать список символов KMI для устройства, используя инструкции из раздела Как работать со списками символов . Многие партнеры предоставляют один список символов для каждого ACK, но это не является жестким требованием. Если это поможет при обслуживании, вы можете отправить несколько списков символов.

Расширить KMI

Хотя символы KMI и связанные с ними структуры остаются стабильными (это означает, что изменения, которые нарушают стабильные интерфейсы в ядре с замороженным KMI, не могут быть приняты), ядро ​​GKI остается открытым для расширений, поэтому устройствам, которые будут выпущены позже в этом году, не нужно будет определять все свои зависимости до заморозки KMI. Чтобы расширить KMI, вы можете добавить в KMI новые символы для новых или существующих экспортированных функций ядра, даже если KMI заморожен. Новые патчи ядра также могут быть приняты, если они не нарушают KMI.

О поломках КМИ

Ядро имеет исходные коды , и на основе этих источников создаются двоичные файлы. Ветви ядра, отслеживаемые с помощью ABI, включают ABI-представление текущего ABI GKI (в виде файла .stg ). После сборки двоичных файлов ( vmlinux , Image и любых модулей GKI) из двоичных файлов можно извлечь представление ABI. Любое изменение, внесенное в исходный файл ядра, может повлиять на двоичные файлы и, в свою очередь, также повлиять на извлеченный .stg . Анализатор AbiAnalyzer сравнивает зафиксированный файл .stg с файлом, извлеченным из артефактов сборки, и устанавливает метку Lint-1 для изменения в Gerrit, если обнаруживает семантическую разницу.

Устранение поломок ABI

Например, следующий патч вносит очень очевидную поломку 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.

Когда вы запускаете сборку ABI с примененным этим патчем, инструмент завершает работу с ненулевым кодом ошибки и сообщает о разнице ABI, аналогичной этой:

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 обнаружены во время сборки

Наиболее распространенной причиной ошибок является использование драйвером нового символа ядра, которого нет ни в одном из списков символов.

Если символ не включен в ваш список символов, вам необходимо сначала убедиться, что он экспортирован с помощью EXPORT_SYMBOL_GPL( symbol_name ) , а затем обновить список символов и представление ABI. Например, следующие изменения добавляют новую функцию Incremental FS в ветку android-12-5.10 , которая включает обновление списка символов и представления ABI.

  • Пример изменения функции находится в aosp/1345659 .
  • Пример списка символов находится в aosp/1346742 .
  • Пример изменения представления ABI находится в aosp/1349377 .

Если символ экспортирован (либо вами, либо он был экспортирован ранее), но никакой другой драйвер его не использует, вы можете получить ошибку сборки, подобную следующей.

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

Чтобы решить эту проблему, обновите список символов KMI как в ядре, так и в ACK (см. Обновление представления ABI ). Пример обновления списка символов и представления ABI в ACK см. в aosp/1367601 .

Устранение сбоев ABI ядра

Вы можете устранить поломки ABI ядра, проведя рефакторинг кода, чтобы не изменять ABI , или обновить представление ABI . Используйте следующую таблицу, чтобы определить лучший подход для вашей ситуации.

Блок-схема поломки ABI

Рисунок 1. Разрешение поломки ABI

Рефакторинг кода, чтобы избежать изменений ABI

Приложите все усилия, чтобы избежать изменения существующего ABI. Во многих случаях вы можете выполнить рефакторинг кода, чтобы удалить изменения, влияющие на ABI.

  • Рефакторинг изменений полей структуры. Если изменение изменяет ABI для функции отладки, добавьте #ifdef вокруг полей (в структурах и ссылках на исходный код) и убедитесь, что CONFIG , используемый для #ifdef , отключен для рабочих defconfig и gki_defconfig . Пример того, как отладочную конфигурацию можно добавить в структуру, не нарушая ABI, можно найти в этом патчсете .

  • Рефакторинг функций, чтобы не менять ядро ​​ядра. Если в ACK необходимо добавить новые функции для поддержки партнерских модулей, попробуйте выполнить рефакторинг части изменения ABI, чтобы избежать изменения ABI ядра. Пример использования существующего ABI ядра для добавления дополнительных возможностей без изменения ABI ядра см. в aosp/1312213 .

Исправьте сломанный ABI на Android Gerrit

Если вы не намеренно сломали ABI ядра, вам необходимо провести расследование, используя рекомендации, предоставляемые инструментами мониторинга ABI. Наиболее распространенными причинами сбоев являются изменения структур данных и связанные с ними изменения CRC символов, а также изменения параметров конфигурации, которые приводят к любому из вышеупомянутых событий. Начните с устранения проблем, обнаруженных инструментом.

Вы можете воспроизвести результаты ABI локально, см. раздел Сборка ядра и его представления ABI .

О этикетках «Линт-1»

Если вы загружаете изменения в ветку, содержащую замороженный или завершенный KMI, изменения должны пройти через AbiAnalyzer , чтобы гарантировать, что изменения не повлияют на стабильный ABI несовместимым образом. Во время этого процесса AbiAnalyzer ищет отчет ABI, созданный во время сборки (расширенная сборка, которая выполняет обычную сборку, а затем некоторые шаги извлечения и сравнения ABI).

Если AbiAnalyzer обнаруживает непустой отчет, он устанавливает метку Lint-1, и изменение блокируется от отправки до тех пор, пока оно не будет решено; пока набор патчей не получит метку Lint+1.

Обновите ABI ядра

Если изменение ABI неизбежно, вы должны применить изменения кода, представление ABI и список символов к ACK. Чтобы Lint удалил -1 и не нарушил совместимость GKI, выполните следующие действия:

  1. Загрузите изменения кода в ACK .

  2. Подождите, пока получите Code-Review +2 для набора исправлений.

  3. Обновите эталонное представление ABI .

  4. Объедините изменения кода и изменение обновления ABI.

Загрузите изменения кода ABI в ACK.

Обновление ACK ABI зависит от типа вносимого изменения.

  • Если изменение ABI связано с функцией, которая влияет на тесты CTS или VTS, это изменение обычно можно выбрать для ACK как есть. Например:

    • aosp/1289677 необходим для работы звука.
    • aosp/1295945 необходим для работы USB.
  • Если изменение ABI касается функции, которую можно использовать совместно с ACK, это изменение можно выбрать в ACK как есть. Например, следующие изменения не нужны для теста CTS или VTS, но их можно передать через ACK:

    • aosp/1250412 — это термическое изменение функции.
    • aosp/1288857 — это изменение EXPORT_SYMBOL_GPL .
  • Если изменение ABI вводит новую функцию, которую не нужно включать в ACK, вы можете ввести символы в ACK с помощью заглушки, как описано в следующем разделе.

Используйте заглушки для ACK

Заглушки должны быть необходимы только для изменений ядра ядра, которые не приносят пользы ACK, например, изменения производительности и мощности. В следующем списке подробно описаны примеры заглушек и частичных выборок в ACK для GKI.

  • Заглушка функции изоляции ядра ( aosp/1284493 ). Возможности ACK не обязательны, но символы должны присутствовать в ACK, чтобы ваши модули могли использовать эти символы.

  • Символ-заполнитель для модуля поставщика ( aosp/1288860 ).

  • Выбор функции отслеживания событий mm для каждого процесса только для ABI ( aosp/1288454 ). Исходный патч был выбран для ACK, а затем обрезан, чтобы включить только необходимые изменения для разрешения различий ABI для task_struct и mm_event_count . Этот патч также обновляет перечисление mm_event_type , чтобы оно содержало конечные члены.

  • Частичная выборка изменений термической структуры ABI, которые требовали большего, чем просто добавление новых полей ABI.

    • Патч aosp/1255544 устранил различия ABI между партнерским ядром и ACK.

    • Патч aosp/1291018 исправил функциональные проблемы, обнаруженные во время тестирования GKI предыдущего патча. Исправление включало инициализацию структуры параметров датчика для регистрации нескольких тепловых зон на одном датчике.

  • CONFIG_NL80211_TESTMODE Изменения ABI ( aosp/1344321 ). Этот патч добавил необходимые изменения в структуру ABI и гарантировал, что дополнительные поля не вызывают функциональных различий, что позволяет партнерам включать CONFIG_NL80211_TESTMODE в свои рабочие ядра и при этом поддерживать соответствие GKI.

Принудительное использование KMI во время выполнения

Ядра GKI используют параметры конфигурации TRIM_UNUSED_KSYMS=y и UNUSED_KSYMS_WHITELIST=<union of all symbol lists> , которые ограничивают экспортируемые символы (например, символы, экспортированные с помощью EXPORT_SYMBOL_GPL() ) теми, которые перечислены в списке символов. Все остальные символы не экспортируются, и загрузка модуля, требующего неэкспортированного символа, запрещена. Это ограничение применяется во время сборки, и отсутствующие записи помечаются.

В целях разработки вы можете использовать сборку ядра GKI, которая не включает обрезку символов (это означает, что можно использовать все обычно экспортируемые символы). Чтобы найти эти сборки, найдите сборки kernel_debug_aarch64 на ci.android.com .

Обеспечьте соблюдение KMI с помощью управления версиями модулей.

Ядра Generic Kernel Image (GKI) используют управление версиями модулей ( CONFIG_MODVERSIONS ) в качестве дополнительной меры для обеспечения соответствия KMI во время выполнения. Управление версиями модулей может привести к сбоям несоответствия циклической избыточной проверки (CRC) во время загрузки модуля, если ожидаемый KMI модуля не соответствует KMI vmlinux . Например, ниже приведен типичный сбой, возникающий во время загрузки модуля из-за несоответствия CRC для символа 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 ''

Использование управления версиями модулей

Управление версиями модулей полезно по следующим причинам:

  • Управление версиями модулей фиксирует изменения в видимости структуры данных. Если модули изменяют непрозрачные структуры данных, то есть структуры данных, которые не являются частью KMI, они ломаются после будущих изменений структуры.

    В качестве примера рассмотрим поле fwnode в struct device . Это поле ДОЛЖНО быть непрозрачным для модулей, чтобы они не могли вносить изменения в поля device->fw_node или делать предположения о его размере.

    Однако если модуль включает <linux/fwnode.h> (прямо или косвенно), то поле fwnode в struct device больше не является для него непрозрачным. Затем модуль может внести изменения в device->fwnode->dev или device->fwnode->ops . Этот сценарий проблематичен по нескольким причинам, следующим образом:

    • Это может нарушить предположения, которые основной код ядра делает о своих внутренних структурах данных.

    • Если будущее обновление ядра изменит struct fwnode_handle (тип данных fwnode ), то модуль больше не будет работать с новым ядром. Более того, stgdiff не покажет никаких различий, поскольку модуль нарушает KMI, напрямую манипулируя внутренними структурами данных способами, которые невозможно уловить, проверяя только двоичное представление.

  • Текущий модуль считается KMI-несовместимым, если он позднее загружен новым несовместимым ядром. Управление версиями модулей добавляет проверку во время выполнения, чтобы избежать случайной загрузки модуля, несовместимого с KMI с ядром. Эта проверка предотвращает трудно поддающиеся отладке проблемы во время выполнения и сбои ядра, которые могут возникнуть из-за необнаруженной несовместимости в KMI.

Включение управления версиями модулей предотвращает все эти проблемы.

Проверьте несоответствие CRC без загрузки устройства.

stgdiff сравнивает и сообщает о несоответствии CRC между ядрами, а также о других различиях ABI.

Кроме того, полная сборка ядра с включенным CONFIG_MODVERSIONS создает файл Module.symvers как часть обычного процесса сборки. В этом файле есть одна строка для каждого символа, экспортируемого ядром ( vmlinux ) и модулями. Каждая строка состоит из значения CRC, имени символа, пространства имен символа, имени vmlinux или модуля, экспортирующего символ, и типа экспорта (например, EXPORT_SYMBOL вместо EXPORT_SYMBOL_GPL ).

Вы можете сравнить файлы Module.symvers между сборкой GKI и вашей сборкой, чтобы проверить наличие различий CRC в символах, экспортируемых vmlinux . Если в каком-либо символе, экспортированном vmlinux , есть разница в значении CRC , и этот символ используется одним из модулей, которые вы загружаете в свое устройство, модуль не загружается.

Если у вас нет всех артефактов сборки, но есть файлы vmlinux ядра GKI и вашего ядра, вы можете сравнить значения CRC для определенного символа, выполнив следующую команду для обоих ядер и сравнив выходные данные:

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

Например, следующая команда проверяет значение CRC для символа module_layout :

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

Устранение несоответствий CRC

Используйте следующие шаги, чтобы устранить несоответствие CRC при загрузке модуля:

  1. Соберите ядро ​​GKI и ядро ​​вашего устройства, используя опцию --kbuild_symtypes , как показано в следующей команде:

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

    Эта команда создает файл .symtypes для каждого файла .o . Подробности см. в разделе KBUILD_SYMTYPES в Kleaf .

    Для Android 13 и более ранних версий создайте ядро ​​GKI и ядро ​​вашего устройства, добавив KBUILD_SYMTYPES=1 к команде, которую вы используете для сборки ядра, как показано в следующей команде:

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

    При использовании build_abi.sh, флаг KBUILD_SYMTYPES=1 уже неявно установлен.

  2. Найдите файл .c , в который экспортируется символ с несоответствием CRC, с помощью следующей команды:

    git -C common grep EXPORT_SYMBOL.*module_layout
    kernel/module/version.c:EXPORT_SYMBOL(module_layout);
  3. Файл .c имеет соответствующий файл .symtypes в GKI и артефакты сборки ядра вашего устройства. Найдите файл .symtypes , используя следующие команды:

    cd bazel-bin/common/kernel_aarch64/symtypes
    ls -1 kernel/module/version.symtypes

    В Android 13 и более ранних версиях при использовании устаревших сценариев сборки местоположение, скорее всего, будет либо out/$BRANCH/common , либо out_abi/$BRANCH/common .

    Каждый файл .symtypes представляет собой обычный текстовый файл, состоящий из описаний типов и символов:

    • Каждая строка представляет собой key description формы, где описание может ссылаться на другие ключи в том же файле.

    • Такие ключи, как [s|u|e|t]#foo относятся к [struct|union|enum|typedef] foo . Например:

      t#bool typedef _Bool bool
      
    • Ключи без префикса x# — это просто имена символов. Например:

      find_module s#module * find_module ( const char * )
      
  4. Сравните два файла и устраните все различия.

Лучше всего генерировать symtypes при сборке непосредственно перед проблемным изменением, а затем при проблемном изменении. Сохранение всех файлов означает, что их можно будет сравнивать массово.

Например,

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

В противном случае просто сравните конкретные интересующие файлы.

Случай 1. Различия из-за видимости типов данных.

Новый #include может добавить новое определение типа (скажем, struct foo ) в исходный файл. В этих случаях его описание в соответствующем файле .symtypes изменится с пустого structure_type foo { } на полное определение.

Это повлияет на все CRC всех символов в файле .symtypes , описания которых прямо или косвенно зависят от определения типа.

Например, добавление следующей строки в файл include/linux/device.h вашего ядра приводит к несоответствию CRC, одно из которых касается module_layout() :

 #include <linux/fwnode.h>

Сравнение module/version.symtypes для этого символа обнаруживает следующие различия:

 $ 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 ] ; }
  ...

Если ядро ​​GKI имеет полное определение типа, но в вашем ядре оно отсутствует (очень маловероятно), то объедините последнее общее ядро ​​Android с вашим ядром, чтобы использовать последнюю версию ядра GKI.

В большинстве случаев в ядре GKI отсутствует полное определение типа в .symtypes , но в вашем ядре оно есть из-за дополнительных директив #include .

Разрешение для Android 16 и выше

Убедитесь, что затронутый исходный файл содержит заголовок стабилизации Android KABI:

#include <linux/android_kabi.h>

Для каждого затронутого типа добавьте ANDROID_KABI_DECLONLY(name); в глобальной области действия к затронутому исходному файлу.

Например, если разница symtypes была такой:

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

Тогда проблема в том, что struct ubuf_info теперь имеет полное определение в symtypes . Решение состоит в том, чтобы добавить строку в drivers/android/vendor_hooks.c :

ANDROID_KABI_DECLONLY(ubuf_info);

Это указывает gendwarfksyms рассматривать именованный тип в файле как неопределенный.

Более сложная возможность заключается в том, что новый #include сам находится в заголовочном файле. В этом случае вам может потребоваться распределить различные наборы вызовов макросов ANDROID_KABI_DECLONLY по исходным файлам, которые косвенно извлекают дополнительные определения типов, поскольку некоторые из них, возможно, уже имели некоторые определения типов.

Для удобства чтения размещайте такие вызовы макросов в начале исходного файла.

Разрешение для Android 15 и ниже

Часто исправление просто скрывает новый #include из genksyms .

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

В противном случае, чтобы определить #include , вызывающий разницу, выполните следующие действия:

  1. Откройте файл заголовка, который определяет символ или тип данных, имеющий эту разницу. Например, отредактируйте include/linux/fwnode.h для struct fwnode_handle .

  2. Добавьте следующий код в начало файла заголовка:

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. В файле .c модуля, который имеет несоответствие CRC, добавьте следующую строку перед любой из строк #include .

    #define CRC_CATCH 1
    
  4. Скомпилируйте свой модуль. Результирующая ошибка времени сборки показывает цепочку заголовочных файлов #include , которая привела к несоответствию CRC. Например:

    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"
    

    Одно из звеньев в этой цепочке #include связано с изменением, внесенным в ваше ядро, которого нет в ядре GKI.

Случай 2: Различия из-за изменений типа данных

Если несоответствие CRC для символа или типа данных не связано с разницей в видимости, то оно связано с фактическими изменениями (добавлениями, удалениями или изменениями) в самом типе данных.

Например, внесение следующего изменения в ядро ​​приводит к нескольким несоответствиям CRC, поскольку изменения этого типа косвенно затрагивают многие символы:

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

Одно несоответствие CRC связано с devm_of_platform_populate() .

Если вы сравните файлы .symtypes для этого символа, это может выглядеть так:

 $ 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 * ) ; ...

Чтобы определить измененный тип, выполните следующие действия:

  1. Найдите определение символа в исходном коде (обычно в файлах .h ).

    • Чтобы узнать о различиях символов между вашим ядром и ядром GKI, найдите коммит, выполнив следующую команду:
    git blame
    • Для удаленных символов (когда символ удален в дереве и вы также хотите удалить его в другом дереве) вам необходимо найти изменение, которое удалило строку. Используйте следующую команду для дерева, из которого была удалена строка:
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
  2. Просмотрите возвращенный список коммитов, чтобы найти изменение или удаление. Вероятно, вы ищете первый коммит. Если это не так, просматривайте список, пока не найдете коммит.

  3. После того, как вы идентифицируете коммит, либо верните его в свое ядро, либо обновите его, чтобы подавить изменение CRC, загрузите его в ACK и объедините . Каждый оставшийся перерыв ЛПИ необходимо будет проверить на предмет безопасности, и при необходимости можно записать разрешенный перерыв.

Предпочитаю использовать существующие отступы

Некоторые структуры в GKI дополнены, чтобы обеспечить их расширение без нарушения существующих модулей поставщика. Если восходящий коммит (например) добавляет элемент в такую ​​структуру, то можно изменить его, чтобы вместо этого использовать часть заполнения. Это изменение затем скрыто от расчета CRC.

Стандартизированный самодокументируемый макрос ANDROID_KABI_RESERVE резервирует (выровненное) пространство размером u64 . Он используется вместо объявления члена.

Например:

struct data {
        u64 handle;
        ANDROID_KABI_RESERVE(1);
        ANDROID_KABI_RESERVE(2);
};

Заполнение может использоваться, не затрагивая CRC символов, с помощью ANDROID_KABI_USE (или ANDROID_KABI_USE2 или других вариантов, которые могут быть определены).

Член sekret доступен так, как если бы он был объявлен напрямую, но на самом деле макрос расширяется до анонимного члена объединения, содержащего sekret , а также все, что используется gendwarfksyms для поддержания стабильности типов символов.

struct data {
        u64 handle;
        ANDROID_KABI_USE(1, void *sekret);
        ANDROID_KABI_RESERVE(2);
};
Разрешение для Android 16 и выше

CRC рассчитываются программой gendwarfksyms , которая использует отладочную информацию DWARF, таким образом поддерживая типы C и Rust. Разрешение зависит от типа изменения типа. Вот несколько примеров.

Новые или измененные счетчики

Иногда добавляются новые перечислители, а иногда также затрагивается значение перечислителя MAX или подобное. Эти изменения безопасны, если они не «ускользают» от GKI или если мы можем быть уверены, что модули поставщика не заботятся об их значениях.

Например:

 enum outcome {
       SUCCESS,
       FAILURE,
       RETRY,
+      TRY_HARDER,
       OUTCOME_LIMIT
 };

Добавление TRY_HARDER и изменение OUTCOME_LIMIT можно скрыть от расчета CRC с помощью вызовов макросов в глобальной области действия:

ANDROID_KABI_ENUMERATOR_IGNORE(outcome, TRY_HARDER);
ANDROID_KABI_ENUMERATOR_VALUE(outcome, OUTCOME_LIMIT, 3);

Для удобства чтения поместите их сразу после определения enum .

Новый элемент конструкции, занимающий существующее отверстие

Из-за выравнивания между urgent и scratch байтами останутся неиспользованные байты.

        void *data;
        bool urgent;
+       bool retry;
        void *scratch;

Добавление retry не влияет на существующее смещение члена или размер структуры. Однако это может повлиять на CRC символов или представление ABI, или на то и другое.

Это скроет его от расчета CRC:

        void *data;
        bool urgent;
+       ANDROID_KABI_IGNORE(1, bool retry);
        void *scratch_space;

retry члена доступен так, как если бы он был объявлен напрямую, но на самом деле макрос расширяется до анонимного члена объединения, содержащего retry , а также вещи, используемые gendwarfksyms для поддержания стабильности символьного типа.

Расширение структуры новыми членами

Члены иногда добавляются в конец структуры. Это не влияет на смещения существующих членов и не влияет на существующих пользователей структуры, которые обращаются к ней только по указателю. Размер структуры влияет на ее CRC, и его изменения можно подавить с помощью дополнительного вызова макроса в глобальной области следующим образом:

struct data {
        u64 handle;
        u64 counter;
        ANDROID_KABI_IGNORE(1, void *sekret);
};

ANDROID_KABI_BYTE_SIZE(data, 16);

Для удобства чтения поместите это сразу после определения struct .

Все остальные изменения типа или типа символа

Очень редко могут возникать изменения, которые не попадают ни в одну из предыдущих категорий, что приводит к изменениям CRC, которые невозможно подавить с помощью предыдущих макросов.

В этих случаях исходное описание типа или symtypes может быть предоставлено с вызовом ANDROID_KABI_TYPE_STRING в глобальной области видимости.

struct data {
        /* extensive changes */
};

ANDROID_KABI_TYPE_STRING("s#data", "original s#data symtypes definition");

Для удобства чтения поместите его сразу после определения типа или символа.

Разрешение для Android 15 и ниже

Изменения типа и типа символа должны быть скрыты от genksyms . Это можно сделать, управляя предварительной обработкой с помощью __GENKSYMS__ .

Таким образом можно выразить произвольные преобразования кода.

Например, чтобы скрыть новый элемент, занимающий дыру в существующей структуре:

struct parcel {
        void *data;
        bool urgent;
#ifndef __GENKSYMS__
        bool retry;
#endif
        void *scratch_space;
};