Мониторинг 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, не могут быть приняты), ядро GKI остаётся открытым для расширений, поэтому устройствам, поставляемым позднее в этом году, не потребуется определять все свои зависимости до заморозки KMI. Чтобы расширить KMI, можно добавить в KMI новые символы для новых или существующих экспортированных функций ядра, даже если KMI заморожен. Новые исправления ядра также могут быть приняты, если они не нарушают KMI.

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

Ядро содержит исходные коды , из которых собираются двоичные файлы. Ветки ядра, отслеживаемые ABI, включают представление текущего ABI GKI (в виде файла .stg ). После сборки двоичных файлов ( vmlinux , Image и любых модулей GKI) из двоичных файлов можно извлечь представление ABI. Любое изменение исходного файла ядра может повлиять на двоичные файлы и, в свою очередь, на извлеченный файл .stg . Анализ соответствия ABI сравнивает зафиксированный файл .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 .

О этикетках Lint-1

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

Каждый из этих анализов ABI может установить метку 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 .
    • Для работы USB необходим aosp/1295945 .
  • Если изменение 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. Исправление включало инициализацию структуры параметров датчика для регистрации нескольких тепловых зон на одном датчике.

  • Изменения ABI CONFIG_NL80211_TESTMODE ( 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 непосредственно перед проблемным изменением, а затем во время него. Сохранение всех файлов позволяет сравнивать их в пакетном режиме.

Например,

diff -r -N -U0 good bad

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

Случай 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 Common Kernel с вашим ядром, чтобы использовать последнюю базу ядра 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 для слияния . Каждое оставшееся нарушение ABI необходимо будет проверить на безопасность, и при необходимости можно зафиксировать допустимое нарушение.

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

Некоторые структуры в 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 для поддержания стабильности symtype.

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

Иногда элементы добавляются в конец структуры. Это не влияет на смещения существующих элементов и не влияет на существующих пользователей структуры, которые обращаются к ней только по указателю. Размер структуры влияет на её контрольную сумму (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;
};