Вы можете использовать инструмент мониторинга двоичного интерфейса приложения (ABI), доступный в Android 11 и более поздних версиях, для стабилизации встроенного ABI ядер Android. Инструментарий собирает и сравнивает представления ABI из существующих двоичных файлов ядра (модули vmlinux
+ GKI). Этими представлениями ABI являются файлы .stg
и списки символов. Интерфейс, на котором представление дает представление, называется интерфейсом модуля ядра (KMI). Вы можете использовать этот инструмент для отслеживания и устранения изменений в KMI.
Инструменты мониторинга ABI разработаны в AOSP и используют STG (или libabigail
в Android 13 и более ранних версиях) для создания и сравнения представлений.
На этой странице описываются инструменты, процесс сбора и анализа представлений ABI, а также использование таких представлений для обеспечения стабильности встроенного ABI. На этой странице также представлена информация о внесении изменений в ядра Android.
Процесс
Анализ ABI ядра состоит из нескольких шагов, большинство из которых можно автоматизировать:
- Создайте ядро и его ABI-представление .
- Анализ различий ABI между сборкой и эталоном .
- Обновите представление ABI (при необходимости) .
- Работа со списками символов .
Следующие инструкции подходят для любого ядра, которое вы можете собрать с помощью поддерживаемой цепочки инструментов (например, предварительно созданной цепочки инструментов Clang). repo manifests
доступны для всех общих ветвей ядра Android и для нескольких ядер для конкретных устройств. Они гарантируют, что при сборке дистрибутива ядра для анализа используется правильная цепочка инструментов.
Списки символов
KMI не включает в себя все символы ядра и даже не все из более чем 30 000 экспортированных символов. Вместо этого символы, которые могут использоваться модулями поставщиков, явно перечислены в наборе файлов списков символов, которые общедоступны в корне дерева ядра. Объединение всех символов во всех файлах списка символов определяет набор символов KMI, который поддерживается как стабильный. Пример файла списка символов — abi_gki_aarch64_db845c , который объявляет символы, необходимые для DragonBoard 845c .
Только символы, перечисленные в списке символов, а также связанные с ними структуры и определения считаются частью KMI. Вы можете опубликовать изменения в списках символов, если нужные вам символы отсутствуют. После того как новые интерфейсы попадают в список символов и являются частью описания KMI, они сохраняются как стабильные и их нельзя удалять из списка символов или изменять после заморозки ветки.
Каждая ветвь ядра Android Common Kernel (ACK) KMI имеет свой собственный набор списков символов. Не предпринимается никаких попыток обеспечить стабильность ABI между различными ветвями ядра KMI. Например, KMI для android12-5.10
полностью независим от KMI для android13-5.10
.
Инструменты ABI используют списки символов KMI, чтобы ограничить, какие интерфейсы необходимо отслеживать на предмет стабильности. Основной список символов содержит символы, необходимые модулям ядра GKI. Ожидается, что поставщики будут предоставлять и обновлять дополнительные списки символов, чтобы гарантировать, что интерфейсы, на которые они полагаются, поддерживают совместимость с ABI. Например, чтобы просмотреть список списков символов для android13-5.15
, обратитесь к https://android.googlesource.com/kernel/common/+/refs/heads/android13-5.15/android
Список символов содержит символы, которые, как сообщается, необходимы для конкретного поставщика или устройства. Полный список, используемый инструментами, представляет собой объединение всех файлов списков символов 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 обнаружены во время сборки
Наиболее распространенной причиной ошибок является использование драйвером нового символа ядра, которого нет ни в одном из списков символов.
Если символ не включен в список символов ( android/abi_gki_aarch64
), вам необходимо сначала убедиться, что он экспортирован с помощью EXPORT_SYMBOL_GPL( symbol_name )
, а затем обновить XML-представление ABI и список символов. Например, следующие изменения добавляют новую функцию Incremental FS в ветку android-12-5.10
, которая включает обновление списка символов и XML-представления ABI.
- Пример изменения функции находится в aosp/1345659 .
- Пример списка символов находится в aosp/1346742 .
- Пример изменения XML 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 ). Пример обновления XML ABI и списка символов в ACK см. в aosp/1367601 .
Устранение сбоев 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, выполните следующие действия:
Подождите, пока получите Code-Review +2 для набора исправлений.
Объедините изменения кода и изменение обновления 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 при загрузке модуля:
Соберите ядро 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
уже неявно установлен.Найдите файл
.c
, в который экспортируется символ с несоответствием CRC, с помощью следующей команды:cd common && git grep EXPORT_SYMBOL.*module_layout kernel/module.c:EXPORT_SYMBOL(module_layout);
Файл
.c
имеет соответствующий файл.symtypes
в GKI и артефакты сборки ядра вашего устройства. Найдите файл.c
используя следующие команды:cd out/$BRANCH/common && ls -1 kernel/module.* kernel/module.o kernel/module.o.symversions kernel/module.symtypes
Ниже приведены характеристики файла
.c
:Формат файла
.c
— одна (потенциально очень длинная) строка на символ.[s|u|e|etc]#
в начале строки означает, что символ имеет тип данных[struct|union|enum|etc]
. Например:t#bool typedef _Bool bool
Отсутствующий префикс
#
в начале строки указывает на то, что символ является функцией. Например:find_module s#module * find_module ( const char * )
Сравните два файла и устраните все различия.
Случай 1. Различия из-за видимости типов данных.
Если одно ядро сохраняет символ или тип данных непрозрачным для модулей, а другое ядро — нет, эта разница проявляется между файлами .symtypes
двух ядер. Файл .symtypes
одного из ядер содержит UNKNOWN
для символа, а файл .symtypes
другого ядра содержит расширенное представление символа или типа данных.
Например, добавление следующей строки в файл include/linux/device.h
вашего ядра приводит к несоответствию CRC, одно из которых касается module_layout()
:
#include <linux/fwnode.h>
Сравнение module.symtypes
для этого символа обнаруживает следующие различия:
$ 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 ] ; }
...
Если ваше ядро имеет значение UNKNOWN
, а ядро GKI имеет расширенное представление символа (очень маловероятно), то объедините последнее общее ядро Android с вашим ядром, чтобы использовать последнюю версию ядра GKI.
В большинстве случаев ядро GKI имеет значение UNKNOWN
, но ваше ядро имеет внутренние детали символа из-за изменений, внесенных в ваше ядро. Это связано с тем, что в один из файлов вашего ядра добавлен #include
, которого нет в ядре GKI.
Часто исправление просто скрывает новый #include
из genksyms
.
#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif
В противном случае, чтобы определить #include
, вызывающий разницу, выполните следующие действия:
Откройте файл заголовка, который определяет символ или тип данных, имеющий эту разницу. Например, отредактируйте
include/linux/fwnode.h
дляstruct fwnode_handle
.Добавьте следующий код в начало файла заголовка:
#ifdef CRC_CATCH #error "Included from here" #endif
В файле
.c
модуля, который имеет несоответствие CRC, добавьте следующую строку в качестве первой строки перед любой из строк#include
.#define CRC_CATCH 1
Скомпилируйте свой модуль. Результирующая ошибка времени сборки показывает цепочку заголовочных файлов
#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.Определите изменение, верните его в свое ядро или загрузите в ACK и объедините .
Случай 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 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 * ) ; ...
Чтобы определить измененный тип, выполните следующие действия:
Найдите определение символа в исходном коде (обычно в файлах
.h
).- Чтобы узнать о различиях символов между вашим ядром и ядром GKI, найдите фиксацию, выполнив следующую команду:
git blame
- Для удаленных символов (когда символ удален в дереве и вы также хотите удалить его в другом дереве) вам необходимо найти изменение, которое удалило строку. Используйте следующую команду для дерева, из которого была удалена строка:
git log -S "copy paste of deleted line/word" -- <file where it was deleted>
Просмотрите возвращенный список коммитов, чтобы найти изменение или удаление. Вероятно, вы ищете первый коммит. Если это не так, просматривайте список, пока не найдете коммит.
После того, как вы обнаружите изменение, либо верните его в свое ядро, либо загрузите в ACK и объедините .