Android 커널 ABI 모니터링

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

Android 11 이상에서 제공되는 Application Binary Interface(ABI) 모니터링 도구를 사용하여 Android 커널의 커널 내 ABI를 안정화할 수 있습니다. 이 도구는 기존 커널 바이너리(vmlinux + 모듈)에서 ABI 표현을 수집하고 비교합니다. 이러한 ABI 표현은 .xml 파일 및 기호 목록입니다. 표현이 뷰를 제공하는 인터페이스를 커널 모듈 인터페이스(KMI)라고 합니다. 도구를 사용하여 KMI 변경사항을 추적하고 완화할 수 있습니다.

ABI 모니터링 도구는 AOSP에서 개발했고 libabigail을 사용하여 표현을 생성하고 비교합니다.

이 페이지에서는 도구와 ABI 표현 수집 및 분석 프로세스, 이러한 표현을 사용하여 커널 내 ABI에 안정성을 제공하는 방법을 설명합니다. Android 커널의 변경사항 기여에 관한 정보도 제공합니다.

이 디렉터리에는 ABI 분석을 위한 특정 도구가 포함되어 있습니다. build_abi.sh에서 제공하는 빌드 스크립트와 함께 사용하세요.

프로세스

커널의 ABI 분석에는 여러 단계가 필요하며 대부분 자동화할 수 있습니다.

  1. repo를 통해 도구 모음, 빌드 스크립트, 커널 소스를 가져옵니다.
  2. 기본 요건을 제공합니다(예: libabigail 라이브러리, 도구 모음).
  3. 커널 및 ABI 표현을 빌드합니다.
  4. 빌드와 참조 간의 ABI 차이를 분석합니다.
  5. ABI 표현을 업데이트합니다(필요한 경우).
  6. 기호 목록을 사용합니다.

다음 안내는 지원되는 도구 모음(예: 사전 빌드된 Clang 도구 모음)을 사용하여 빌드할 수 있는 커널에 모두 적용됩니다. repo manifests는 모든 Android 일반 커널 브랜치와 여러 기기별 커널에 사용할 수 있으며 분석을 위해 커널 배포를 빌드할 때 올바른 도구 모음이 사용되도록 합니다.

ABI 모니터링 도구 사용

1. repo를 통해 도구 모음, 빌드 스크립트, 커널 소스 가져오기

repo를 사용하여 도구 모음, 빌드 스크립트(이러한 스크립트), 사전 빌드된 바이너리, 커널 소스를 가져올 수 있습니다. 자세한 내용은 Android 커널 빌드의 해당 정보를 참고하세요.

프로세스를 보여주기 위해 다음 단계에서는 common-android12-5.10을 사용합니다. 이는 Android 커널 분기로, 이 문서 작성 시점의 최신 버전 GKI 커널입니다. repo를 통해 이 분기를 가져오려면 다음을 실행합니다.

repo init -u https://android.googlesource.com/kernel/manifest -b common-android12-5.10
repo sync

2. 커널 및 ABI 표현 빌드

이제 정확한 도구 모음으로 커널을 빌드하고 바이너리(vmlinux + 모듈)에서 ABI 표현을 추출할 수 있습니다.

일반적인 Android 커널 빌드 프로세스(build.sh 사용)와 마찬가지로 이 단계에서는 build_abi.sh를 실행해야 합니다.

BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh

그러면 커널이 빌드되고 ABI 표현이 out_abi 하위 디렉터리로 추출됩니다. 이 경우 out/android12-5.10/dist/abi.xmlout_abi/android12-5.10/dist/abi-<id>.xml의 심볼릭 링크입니다. <id>는 커널 소스 트리를 대상으로 git describe를 실행하여 계산됩니다.

Android 13 브랜치 이상에서는 커널 빌드에서 Bazel이 사용 설정됩니다. 위의 명령어에 해당하는 Bazel 명령어는 다음과 같습니다.

tools/bazel run //common:kernel_aarch64_abi_dist

자세한 내용은 Bazel을 사용한 ABI 모니터링 지원(GKI)Bazel을 사용한 ABI 모니터링 지원(기기 커널)을 참고하세요.

3. 빌드와 참조 표현 간의 ABI 차이 분석

build_abi.sh는 환경 변수 ABI_DEFINITION을 통해 참조가 제공될 때 모든 ABI 차이를 분석하고 보고합니다. ABI_DEFINITION은 커널 소스 트리와 관련된 참조 파일을 가리켜야 하며 명령줄에서 지정하거나 더 일반적으로는 build.config의 값으로 지정할 수 있습니다. 다음을 예로 들 수 있습니다.

BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh

위 명령어에서 build.config.gki.aarch64는 참조 파일을 ABI_DEFINITION=android/abi_gki_aarch64.xml로 정의하고 diff_abiabidiff를 호출하여 새로 생성된 ABI 표현을 참조 파일과 비교합니다. build_abi.sh는 보고서의 위치를 출력하고 모든 ABI 중단에 관한 간단한 보고서를 내보냅니다. 중단이 감지되면 build_abi.sh가 종료되며 0이 아닌 종료 코드를 반환합니다.

Android 13 브랜치 이상에서는 커널 빌드에서 Bazel이 사용 설정됩니다. 위의 명령어에 해당하는 Bazel 명령어는 다음과 같습니다.

tools/bazel run //common:kernel_aarch64_abi_dist

자세한 내용은 Bazel을 사용한 ABI 모니터링 지원(GKI)Bazel을 사용한 ABI 모니터링 지원(기기 커널)을 참고하세요.

4. ABI 표현 업데이트(필요한 경우)

ABI 표현을 업데이트하려면 --update 플래그를 사용하여 build_abi.sh를 호출합니다. 그러면 상응하는 abi.xml 파일(build.config에서 정의됨)이 업데이트됩니다. 업데이트로 인한 ABI 차이를 출력하려면 --print-report로 스크립트를 호출합니다. abi.xml 파일을 업데이트할 때는 커밋 메시지에 보고서를 포함해야 합니다.

Android 13 브랜치 이상에서는 커널 빌드에서 Bazel이 사용 설정됩니다. GKI용 ABI 표현을 업데이트하는 Bazel 명령어는 다음과 같습니다.

tools/bazel run //common:kernel_aarch64_abi_update_kmi_symbol_list &&
tools/bazel run //common:kernel_aarch64_abi_update

자세한 내용은 Bazel을 사용한 ABI 모니터링 지원(GKI)Bazel을 사용한 ABI 모니터링 지원(기기 커널)을 참고하세요.

5. 기호 목록 사용

KMI 기호 목록으로 build_abi.sh를 매개변수화하여 ABI 추출 중에 기호를 필터링합니다. 관련 ABI 커널 기호를 나열하는 일반 텍스트 파일입니다. 예를 들어 다음 콘텐츠가 포함된 기호 목록 파일은 ABI 분석을 이름이 symbol1symbol2인 ELF 기호로 제한합니다.

[abi_symbol_list]
   symbol1
   symbol2

다른 ELF 기호의 변경사항은 고려되지 않습니다. 커널 소스 디렉터리($KERNEL_DIR)에 상대적인 파일로 KMI_SYMBOL_LIST=를 사용하여 상응하는 build.config 구성 파일에 기호 목록 파일을 지정할 수 있습니다. 조직 수준을 제공하려면 build.config 파일에서 ADDITIONAL_KMI_SYMBOL_LISTS=를 사용하여 추가 기호 목록 파일을 지정하면 됩니다. 이렇게 하면 $KERNEL_DIR에 상대적인 추가 기호 목록 파일을 지정합니다. 공백으로 여러 파일 이름을 구분합니다.

기호 목록을 처음 만들거나 기존 기호 목록을 업데이트하려면 build_abi.sh 스크립트를 --update-symbol-list 매개변수와 함께 사용해야 합니다.

스크립트가 적절한 구성으로 실행되면 커널을 빌드하고, vmlinux 및 GKI 모듈에서 내보내며 이와 함께 트리의 다른 모듈에 필요한 기호를 추출합니다.

다음 기호를 내보내는 vmlinux를 고려해보세요(일반적으로 EXPORT_SYMBOL* 매크로를 통해 실행됨).

  func1
  func2
  func3

또한 다음 기호가 필요한 공급업체 모듈 modA.komodB.ko 두 개가 있다고 가정해보겠습니다. 즉, 기호 테이블에 undefined 기호 항목이 나열되어 있습니다.

 modA.ko:    func1 func2
 modB.ko:    func2

ABI 안정성 측면에서 func1func2는 외부 모듈에서 사용되기 때문에 안정적으로 유지되어야 합니다. 반대로 func3은 내보내지지만 어떤 모듈에서도 적극적으로 사용되지 않습니다(즉, 필수가 아님). 따라서 기호 목록에는 func1func2만 포함됩니다.

기호 목록을 만들거나 기존 목록을 업데이트하려면 build_abi.sh를 다음과 같이 실행해야 합니다.

BUILD_CONFIG=path/to/build.config.device build/build_abi.sh --update-symbol-list

이 예에서 build.config.device에는 여러 구성 옵션이 포함되어야 합니다.

  • vmlinuxFILES 목록에 있어야 합니다.
  • 업데이트하려면 KMI_SYMBOL_LIST를 설정하고 KMI 기호 목록을 가리켜야 합니다.
  • GKI_MODULES_LIST가 설정되고 GKI 모듈 목록을 가리켜야 합니다. 이 경로는 일반적으로 android/gki_aarch64_modules입니다.

하위 수준 ABI 도구 사용

대부분의 사용자는 build_abi.sh만 사용하면 됩니다. 경우에 따라 하위 수준 ABI 도구를 직접 사용해야 할 수도 있습니다. build_abi.sh에서 사용하는 두 가지 명령어 dump_abidiff_abi는 ABI 파일을 추출하고 비교하는 데 사용할 수 있습니다. 사용법은 다음 섹션을 참고하세요.

커널 트리에서 ABI 표현 만들기

vmlinux 및 커널 모듈이 빌드된 Linux 커널 트리가 제공된 경우 dump_abi 도구는 선택된 ABI 도구를 사용하여 ABI 표현을 만듭니다. 샘플 호출은 다음과 같습니다.

dump_abi --linux-tree path/to/out --out-file /path/to/abi.xml

abi.xml 파일에는 주어진 디렉터리에 있는 커널 모듈, 그리고 결합되고 관찰 가능한 vmlinux ABI의 텍스트 ABI 표현이 포함되어 있습니다. 이 파일은 수동 검사, 추가 분석에 사용되거나 ABI 안정성을 강화하기 위한 참조 파일로 사용될 수 있습니다.

ABI 표현 비교

dump_abi로 만든 ABI 표현은 diff_abi와 비교할 수 있습니다. dump_abidiff_abi에 모두 같은 abi-tool을 사용합니다. 샘플 호출은 다음과 같습니다.

diff_abi --baseline abi1.xml --new abi2.xml --report report.out

생성된 보고서에는 KMI에 영향을 미치는 감지된 ABI 변경사항이 나열됩니다. baselinenew로 지정된 파일은 dump_abi로 수집된 ABI 표현입니다. diff_abi는 기본 도구의 종료 코드를 전파하므로 비교된 ABI가 호환되지 않을 때 0이 아닌 값을 반환합니다.

KMI 표현 및 기호 필터링

dump_abi로 만든 표현을 필터링하거나 diff_abi와 비교하여 기호를 필터링하려면 KMI 기호 목록 파일 경로를 사용하는 --kmi-symbol-list 매개변수를 사용합니다.

dump_abi --linux-tree path/to/out --out-file /path/to/abi.xml --kmi-symbol-list /path/to/symbol_list_file

기호 목록 사용

KMI에는 커널의 모든 기호가 포함되어 있지는 않으며, 30,000개 이상의 내보낸 기호도 모두 포함되어 있지는 않습니다. 대신 모듈에서 사용할 수 있는 기호는 커널 트리의 루트에 공개적으로 유지되는 기호 목록 파일 세트에 명시적으로 나열됩니다. 모든 기호 목록 파일에 있는 모든 기호의 공용체는 안정된 상태로 유지되는 KMI 기호 세트를 정의합니다. 기호 목록 파일의 예는 abi_gki_aarch64_db845c로, DragonBoard 845c에 필요한 기호를 선언합니다.

기호 목록에 나열된 기호와 관련 구조 및 정의만 KMI의 일부로 간주됩니다. 필요한 기호가 없다면 기호 목록에 변경사항을 게시할 수 있습니다. 새 인터페이스가 기호 목록에 있고 따라서 KMI 설명의 일부가 된 이후에 이 인터페이스는 안정적인 상태로 유지되므로 브랜치가 고정된 후 기호 목록에서 제거되거나 수정되어서는 안 됩니다.

각 Android 일반 커널(ACK) KMI 커널 브랜치에는 고유한 기호 목록 세트가 있습니다. 서로 다른 KMI 커널 브랜치 간에 ABI 안정성을 제공하려고 시도하지 않습니다. 예를 들어 android12-5.10용 KMI와 android13-5.10용 KMI는 완전히 별개입니다.

ABI 도구는 KMI 기호 목록을 사용하여 안정성을 위해 모니터링해야 하는 인터페이스를 제한합니다. 기본 기호 목록에는 GKI 커널 모듈에 필요한 기호가 포함되어 있습니다. 공급업체는 사용하는 인터페이스가 ABI 호환성을 유지하도록 추가 기호 목록을 제출하고 업데이트해야 합니다. 예를 들어 android13-5.15 기호 목록의 목록을 보려면 https://android.googlesource.com/kernel/common/+/refs/heads/android13-5.15/android를 참고하세요.

기호 목록에는 특정 공급업체나 기기에 필요한 것으로 보고된 기호가 포함되어 있습니다. 도구에서 사용하는 전체 목록은 모든 KMI 기호 목록 파일의 공용체입니다. ABI 도구는 함수 서명, 중첩 데이터 구조 등 각 기호의 세부정보를 결정합니다.

KMI가 고정되면 기존 KMI 인터페이스는 변경할 수 없습니다(안정적임). 그러나 추가 시 기존 ABI의 안정성에 영향을 미치지 않는 한 공급업체는 언제든지 KMI에 기호를 추가해도 됩니다. 새로 추가된 기호는 KMI 기호 목록에 인용되는 즉시 안정적으로 유지됩니다. 기호는 해당 기호의 종속 항목과 함께 제공된 기기가 없다고 확인되지 않는 한 커널 목록에서 삭제해서는 안 됩니다.

*.ko 빌드 아티팩트에서 기호 종속 항목을 추출하는 build/abi/extract_symbols 유틸리티를 사용하여 기기의 KMI 기호 목록을 생성할 수 있습니다. 이 유틸리티는 주석 형태로 출력에 주석을 추가하므로 기호의 사용자를 식별하는 데 유용합니다. ACK에 기호 목록을 제출할 때 검토 프로세스를 간소화하기 위해 이러한 주석을 유지하는 것이 좋습니다. 주석을 생략하려면 extract_symbols 스크립트를 실행할 때 --skip-module-grouping 옵션을 전달합니다. 많은 파트너가 ACK당 기호 목록 하나를 제출하지만 이는 엄격하게 적용되는 요구사항은 아닙니다. 유지보수에 도움이 된다면 여러 기호 목록을 제출해도 됩니다.

기호 목록 맞춤설정 및 디버깅과 상세한 분석을 위한 상위 수준 및 하위 수준 ABI 도구 사용에 관한 도움말은 Android 커널용 ABI 모니터링을 참고하세요.

KMI 확장

KMI 기호 및 관련 구조는 안정적인 상태로 유지되지만(고정된 KMI가 있는 커널의 안정적인 인터페이스를 중단시키는 변경사항은 허용되지 않음) GKI 커널은 확장 프로그램에 개방된 상태로 유지되므로 올해 후반기에 제공되는 기기는 KMI가 고정되기 전에 모든 종속 항목을 정의할 필요가 없습니다. KMI를 확장하려면 KMI가 고정되었더라도 내보낸 새 커널 함수나 기존 커널 함수의 경우 KMI에 새 기호를 추가하면 됩니다. 새 커널 패치는 KMI를 중단하지 않는 경우 허용됩니다.

KMI 중단 정보

커널에는 소스가 있고 바이너리는 이러한 소스를 기반으로 빌드됩니다. ABI 모니터링 커널 브랜치에는 현재 GKI ABI를 나타내는 abi.xml이 포함되어 있습니다. 바이너리가 빌드되면(커널 바이너리, vmlinux, Image, 커널 모듈) 바이너리에서 abi.xml 파일을 추출할 수 있습니다. 커널 소스를 변경하면 바이너리가 변경될 수 있고 추출된 abi.xml(변경사항을 적용하고 커널을 빌드한 후 추출된 파일)도 변경될 수 있습니다. AbiAnalyzer 분석 도구는 abi.xml 파일 두 개를 의미론적으로 비교하고 문제가 발견되면 변경사항에 린트-1 라벨을 설정합니다.

ABI 중단 처리

예를 들어 다음 패치는 매우 명백한 ABI 중단을 보여줍니다.

 diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
  index 5ed8f6292a53..f2ecb34c7645 100644
  --- a/include/linux/mm_types.h
  +++ b/include/linux/mm_types.h
  @@ -339,6 +339,7 @@ struct core_state {
   struct kioctx_table;
   struct mm_struct {
      struct {
  +       int dummy;
          struct vm_area_struct *mmap;            /* list of VMAs */
          struct rb_root mm_rb;
          u64 vmacache_seqnum;                   /* per-thread vmacache */

이 패치가 적용된 상태에서 build_abi.sh를 다시 실행하면 도구가 0이 아닌 오류 코드와 함께 종료되고 다음과 같은 ABI 차이가 보고됩니다.

 Leaf changes summary: 1 artifact changed
  Changed leaf types summary: 1 leaf type changed
  Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 0 Added function
  Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable

  'struct mm_struct at mm_types.h:372:1' changed:
    type size changed from 6848 to 6912 (in bits)
    there are data member changes:
  [...]

빌드 시간에 ABI 차이 감지

오류가 발생하는 가장 일반적인 이유는 드라이버가 기호 목록에 없는 커널의 새 기호를 사용하는 경우입니다.

기호가 기호 목록(android/abi_gki_aarch64)에 포함되지 않은 경우 먼저 EXPORT_SYMBOL_GPL(symbol_name)를 사용하여 내보낸 기호인지 확인한 후 ABI XML 표현 및 기호 목록을 업데이트해야 합니다. 예를 들어 다음 변경사항은 기호 목록과 ABI XML 표현을 업데이트하는 작업이 포함된 android-12-5.10 브랜치에 새로운 증분 FS 기능을 추가합니다.

  • 기능 변경 예는 aosp/1345659에서 확인할 수 있습니다.
  • 기호 목록 예는 aosp/1346742에서 확인할 수 있습니다.
  • ABI XML 변경 예는 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

이 문제를 해결하려면 커널과 ACK에서 모두 KMI 기호 목록을 업데이트하세요(ABI 표현 업데이트 참고). ACK에서 ABI XML 및 기호 목록을 업데이트하는 예는 aosp/1367601을 참고하세요.

커널 ABI 중단 해결

ABI를 변경하지 않도록 코드를 리팩터링하거나 ABI 표현을 업데이트하여 커널 ABI 중단을 처리할 수 있습니다. 다음 차트를 사용하여 상황에 가장 적합한 접근 방식을 결정하세요.

ABI 중단 흐름 차트

그림 1. ABI 중단 해결

코드를 리팩터링하여 ABI 변경 방지

기존 ABI를 수정하지 않도록 최선을 다하세요. 대부분의 경우 코드를 리팩터링하여 ABI에 영향을 주는 변경사항을 삭제할 수 있습니다.

  • 구조체 필드 변경사항을 리팩터링합니다. 변경사항으로 인해 디버그 기능의 ABI가 수정되는 경우 필드 주위에 #ifdef를 추가하고(구조체 및 소스 참조에서) #ifdef에 사용된 CONFIG가 프로덕션 defconfig 및 gki_defconfig에 사용 중지되어 있는지 확인합니다. ABI를 중단하지 않고 디버그 구성을 구조체에 추가하는 방법에 관한 예는 이 패치 세트를 참고하세요.

  • 핵심 커널을 변경하지 않도록 기능을 리팩터링합니다. 파트너 모듈을 지원하기 위해 새로운 기능을 ACK에 추가해야 하는 경우 커널 ABI를 수정하지 않도록 변경사항의 ABI 부분을 리팩터링해 보세요. 커널 ABI를 변경하지 않고 기존 커널 ABI를 사용하여 기능을 추가하는 방법에 관한 예는 aosp/1312213을 참고하세요.

Android Gerrit에서 중단된 ABI 수정

의도적으로 커널 ABI를 중단하지 않았다면 ABI 모니터링 도구에서 제공하는 안내에 따라 조사해야 합니다. 가장 일반적인 중단 원인은 추가되거나 삭제된 함수, 변경된 데이터 구조 또는 앞서 언급된 것 중 하나로 이어지는 구성 옵션을 추가하여 발생된 ABI 변경입니다. 먼저 도구에서 발견한 문제를 해결합니다.

build/build.sh 실행에 사용한 같은 인수로 다음 명령어를 실행하여 ABI 테스트를 로컬로 재현할 수 있습니다.

다음은 GKI 커널의 명령어 예입니다.

BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh

린트-1 라벨 정보

고정되거나 완료된 KMI가 포함된 브랜치에 변경사항을 업로드하는 경우 호환되지 않는 방식으로 안정적인 ABI에 영향을 미치지 않도록 변경사항은 ABIAnalyzer를 통과해야 합니다. 이 과정에서 ABIAnalyzer는 빌드(일반 빌드를 실행한 후 일부 ABI 추출 및 비교 단계를 실행하는 확장 빌드) 중에 생성된 ABI 보고서를 찾습니다. ABIAnalyzer가 비어 있지 않은 보고서를 발견하면 린트-1 라벨을 설정하고 해결될 때까지(패치 세트가 린트+1 라벨을 수신할 때까지) 변경사항 제출이 차단됩니다.

커널 ABI 업데이트

커널 ABI 표현을 업데이트해야 한다면 커널 소스 트리에서 상응하는 abi.xml 파일을 업데이트해야 합니다. 이를 위한 가장 간편한 방법은 다음과 같이 build/build_abi.sh를 사용하는 것입니다.

build/build_abi.sh --update --print-report

build/build.sh를 실행하는 데 사용한 같은 인수를 사용합니다. 그러면 소스 트리에서 올바른 abi.xml이 업데이트되고 감지된 차이가 출력됩니다. 연습 삼아 출력된 짧은 보고서를 커밋 메시지에 포함합니다(부분만이라도).

ABI 표현 업데이트

ABI를 꼭 수정해야 하면 코드 변경사항과 ABI XML 및 기호 목록을 ACK에 적용해야 합니다. 린트에서 -1을 삭제하고 GKI 호환성을 손상하지 않도록 하려면 다음 단계를 따르세요.

  1. ABI 코드 변경사항을 ACK에 업로드합니다.

  2. ACK ABI 파일을 업데이트합니다.

  3. 코드 변경사항과 ABI 업데이트 변경사항을 병합합니다.

ACK에 ABI 코드 변경사항 업로드

ACK ABI 업데이트는 변경사항의 유형에 따라 다릅니다.

  • ABI 변경사항이 CTS나 VTS 테스트에 영향을 미치는 기능과 관련이 있는 경우 일반적으로 변경사항을 있는 그대로 ACK로 선별할 수 있습니다. 예를 들면 다음과 같습니다.

  • ABI 변경사항이 ACK와 공유할 수 있는 기능에 관한 것이면 이 변경사항을 ACK로 있는 그대로 선별할 수 있습니다. 예를 들어 다음 변경사항은 CTS나 VTS 테스트에는 필요하지 않지만 ACK와 공유하는 것은 괜찮습니다.

  • ABI 변경에서 ACK에 포함할 필요가 없는 새로운 기능을 도입하면 다음 섹션에 설명된 대로 스텁을 사용하여 기호를 ACK에 도입할 수 있습니다.

ACK에 스텁 사용

스텁은 성능 및 전원 변경사항 등 ACK에 도움이 되지 않는 핵심 커널 변경사항에만 필요해야 합니다. 다음 목록은 GKI의 ACK에서 스텁 및 부분 선별의 예를 자세히 설명합니다.

  • 핵심 격리 기능 스텁(aosp/1284493). ACK의 이 기능은 필요하지 않지만 기호는 모듈에서 이러한 기호를 사용하려면 ACK에 있어야 합니다.

  • 공급업체 모듈의 자리표시자 기호(aosp/1288860)

  • 프로세스별 mm 이벤트 추적 기능의 ABI 전용 선별(aosp/1288454). 원래 패치는 ACK로 선별한 다음 task_structmm_event_count의 ABI 차이를 해결하는 데 필요한 변경사항만 포함하도록 다듬었습니다. 이 패치는 또한 최종 멤버를 포함하도록 mm_event_type enum을 업데이트합니다.

  • 새로운 ABI 필드 추가 이상이 필요한 열 구조체 ABI 변경사항의 일부 선별

    • 패치 aosp/1255544를 통해 파트너 커널과 ACK 간의 ABI 차이를 해결했습니다.

    • 패치 aosp/1291018을 통해 이전 패치의 GKI 테스트 중에 발견된 기능 문제를 수정했습니다. 수정에는 여러 열 영역을 단일 센서에 등록하기 위한 센서 매개변수 구조체 초기화가 포함되었습니다.

  • CONFIG_NL80211_TESTMODE ABI가 변경되었습니다(aosp/1344321). 이 패치를 통해 ABI에 필요한 구조체 변경사항을 추가하고 추가 필드로 인해 기능상의 차이가 발생하지 않도록 했습니다. 따라서 파트너가 프로덕션 커널에 CONFIG_NL80211_TESTMODE를 포함하고 GKI 규정 준수를 계속 유지할 수 있습니다.

ACK ABI 파일 업데이트

ACK ABI 파일을 업데이트하는 방법은 다음과 같습니다.

  1. ABI 변경사항을 업로드하고 패치 세트의 코드 검토 +2를 받을 때까지 기다립니다.

  2. ACK ABI 파일을 업데이트합니다.

    cp partner kernel/android/abi_gki_aarch64_partner ACK kernel/abi_gki_aarch64_partner
    BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh --update
    # Or, with Bazel,
    tools/bazel run //common:kernel_aarch64_abi_update_symbol_list &&
    tools/bazel run //common:kernel_aarch64_abi_update
    
  3. ABI 업데이트를 커밋합니다.

    cd common
    git add android/abi*
    git commit -s -F $DIST_DIR/abi.report.short
    <push to gerrit>
    

    $DIST_DIR/abi.report.short에는 변경사항에 관한 간단한 보고서가 포함됩니다. git commit과 함께 -F 플래그를 사용하면 커밋 텍스트의 보고서가 자동으로 사용됩니다. 그러면 수정하여 제목 줄을 추가하거나 메시지가 너무 길면 잘라낼 수 있습니다.

사전 정의된 ABI가 있는 Android 커널 브랜치

일부 커널 브랜치는 소스 배포의 일부로 Android용 사전 정의된 ABI 표현과 함께 제공됩니다. 이러한 ABI 표현은 정확하고 개발자가 직접 실행한 것처럼 build_abi.sh의 결과를 반영하게 되어 있습니다. ABI는 다양한 커널 구성 옵션의 영향을 많이 받으므로 이러한 .xml 파일은 일반적으로 특정 구성에 속합니다. 예를 들어 common-android12-5.10 분기에는 build.config.gki.aarch64를 사용할 때 빌드 결과에 상응하는 abi_gki_aarch64.xml이 포함되어 있습니다. 특히 build.config.gki.aarch64ABI_DEFINITION을 통해 이 파일도 참조합니다.

이러한 사전 정의된 ABI 표현은 diff_abi와 비교할 때 기준 정의로 사용됩니다. 예를 들어 ABI 변경사항과 관련하여 커널 패치를 검증하려면 패치가 적용된 ABI 표현을 만들고 diff_abi를 사용하여 특정 소스 트리나 구성의 예상 ABI와 비교합니다. ABI_DEFINITION이 설정되어 있으면 build_abi.sh를 적절히 실행하면 됩니다.

런타임에 KMI 적용

GKI 커널은 내보낸 기호(예: EXPORT_SYMBOL_GPL()을 사용하여 내보낸 기호)를 기호 목록에 나열된 기호로 제한하는 TRIM_UNUSED_KSYMS=yUNUSED_KSYMS_WHITELIST=<union of all symbol lists> 구성 옵션을 사용합니다. 다른 모든 기호는 내보내지지 않으며 내보내지 않은 기호가 필요한 모듈을 로드하는 것은 거부됩니다. 이 제한은 빌드 시간에 적용되며 누락된 항목은 신고됩니다.

개발을 위해 기호 자르기가 포함되지 않은 GKI 커널 빌드를 사용할 수 있습니다(즉, 일반적으로 내보낸 모든 기호를 사용할 수 있음). 이러한 빌드를 찾으려면 ci.android.com에서 kernel_debug_aarch64 빌드를 찾으세요.

모듈 버전 관리를 사용하여 KMI 적용

일반 커널 이미지(GKI) 커널은 런타임 시 KMI 규정 준수를 시행하는 추가 조치로 모듈 버전 관리(CONFIG_MODVERSIONS)를 사용합니다. 모듈 버전 관리에서는 모듈의 예상 KMI가 vmlinux KMI와 일치하지 않으면 모듈 로드 시간에 주기적 중복 검사(CRC) 불일치 오류가 발생할 수 있습니다. 예를 들어 다음은 module_layout() 기호의 CRC 불일치로 인해 모듈 로드 시간에 발생하는 일반적인 오류입니다.

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의 일부가 아닌 데이터 구조를 변경하는 경우 향후 구조가 변경되면 모듈이 중단됩니다.

    예를 들어 struct devicefwnode 필드를 고려해 보세요. 이 필드는 모듈이 device->fw_node의 필드를 변경하거나 크기를 가정할 수 없도록 모듈에 불투명해야 합니다(MUST).

    그러나 모듈에 <linux/fwnode.h>가 포함되어 있으면(직접 또는 간접적으로) struct devicefwnode 필드는 더 이상 모듈에 불투명하지 않습니다. 그러면 모듈은 device->fwnode->devdevice->fwnode->ops를 변경할 수 있습니다. 이 시나리오는 다음과 같이 설명된 여러 가지 이유로 문제가 있습니다.

    • 코어 커널 코드의 내부 데이터 구조에 관한 가정이 중단될 수 있습니다.

    • 향후 커널 업데이트에서 struct fwnode_handle(fwnode의 데이터 유형)을 변경하면 모듈이 더 이상 새 커널과 호환되지 않습니다. 또한 abidiff는 어떤 차이점도 표시하지 않습니다. 모듈이 바이너리 표현을 검사하는 것만으로는 캡처할 수 없는 방식으로 내부 데이터 구조를 직접 조작하여 KMI를 중단하기 때문입니다.

  • 현재 모듈은 호환되지 않는 새 커널에서 나중에 로드될 때 KMI 호환되지 않는 것으로 간주됩니다. 모듈 버전 관리는 커널과 KMI 호환되지 않는 모듈을 실수로 로드하지 않도록 런타임 검사를 추가합니다. 이러한 검사는 디버그하기 어려운 런타임 문제와 KMI에서 감지되지 않는 비호환성으로 인해 발생할 수 있는 커널 비정상 종료를 방지합니다.

  • abidiff에는 CONFIG_MODVERSIONS가 포착할 수 있는 복잡한 특정 사례의 ABI 차이를 식별하는 데 제한이 있습니다.

모듈 버전 관리를 사용 설정하면 이러한 문제가 모두 방지됩니다.

기기를 부팅하지 않고 CRC 불일치 확인

abidiff는 커널 간의 CRC 불일치를 비교하고 보고합니다. 이 도구를 사용하면 다른 ABI 차이와 동시에 일치하지 않는 CRC를 포착할 수 있습니다.

또한 CONFIG_MODVERSIONS가 사용 설정된 전체 커널 빌드는 일반적인 빌드 프로세스의 일부로 Module.symvers 파일을 생성합니다. 이 파일에는 커널(vmlinux)과 모듈에서 내보낸 기호마다 한 줄이 있습니다. 각 줄은 CRC 값, 기호 이름, 기호 네임스페이스, 기호를 내보내는 vmlinux 또는 모듈 이름, 내보내기 유형(예: EXPORT_SYMBOL, EXPORT_SYMBOL_GPL)으로 구성됩니다.

GKI 빌드와 개발자 빌드 간의 Module.symvers 파일을 비교하여 vmlinux에서 내보낸 기호의 CRC 차이를 확인할 수 있습니다. vmlinux에서 내보낸 기호에 CRC 값 차이가 있고 그리고 기기에 로드하는 모듈 중 하나에서 이 기호를 사용하는 경우 모듈은 로드되지 않습니다.

빌드 아티팩트가 모두 있지는 않고 GKI 커널과 개발자 커널의 vmlinux 파일은 있다면 두 커널에서 모두 다음 명령어를 실행하여 특정 기호의 CRC 값을 비교할 수 있습니다. 그런 다음 출력을 비교합니다.

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

예를 들어 다음 명령어는 module_layout 기호의 CRC 값을 확인합니다.

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

CRC 불일치 해결

모듈을 로드할 때 CRC 불일치를 해결하려면 다음 단계를 따르세요.

  1. 다음 명령어와 같이 커널을 빌드하는 데 사용하는 명령어 앞에 KBUILD_SYMTYPES=1을 추가하여 GKI 커널과 기기 커널을 빌드합니다.

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

    이 명령어는 각 .o 파일의 .symtypes 파일을 생성합니다. build_abi.sh,를 사용하면 KBUILD_SYMTYPES=1 플래그가 이미 암시적으로 설정됩니다.

    Android 13 브랜치 이상에서는 커널 빌드에서 Bazel이 사용 설정됩니다. 위의 명령어에 해당하는 Bazel 명령어는 다음과 같습니다.

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

    자세한 내용은 Kleaf의 KBUILD_SYMTYPES를 참조하세요.

  2. 다음 명령어를 사용하여 CRC 불일치가 발생한 기호가 내보내진 .c 파일을 찾습니다.

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
    
  3. .c 파일에는 GKI의 상응하는 .symtypes 파일과 기기 커널 빌드 아티팩트가 있습니다. 다음 명령어를 사용하여 .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 * )
      
  4. 두 파일을 비교하여 차이점을 모두 수정합니다.

사례 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이지만 개발자 커널의 변경사항으로 인해 개발자의 커널에는 기호에 관한 내부 세부정보가 있습니다. 커널의 파일 중 하나가 GKI 커널에 없는 #include를 추가했기 때문입니다.

차이의 원인이 되는 #include를 식별하려면 다음 단계를 따르세요.

  1. 이러한 차이가 있는 기호 또는 데이터 유형을 정의하는 헤더 파일을 엽니다. 예를 들어 struct fwnode_handleinclude/linux/fwnode.h를 수정합니다.

  2. 헤더 파일 상단에 다음 코드를 추가합니다.

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. CRC 불일치가 있는 모듈의 .c 파일에서 #include 줄 앞에 다음을 첫 번째 줄로 추가합니다.

    #define CRC_CATCH 1
    
  4. 모듈을 컴파일합니다. 결과 빌드 시간 오류는 이 CRC 불일치를 일으킨 헤더 파일 #include의 체인을 보여줍니다. 예를 들면 다음과 같습니다.

    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 커널에는 없는 변경사항입니다.

  5. 변경사항을 식별하고 커널에서 되돌리거나 ACK에 업로드하여 병합되도록 합니다.

사례 2: 데이터 유형 변경으로 인한 차이

기호 또는 데이터 유형의 CRC 불일치가 공개 상태의 차이로 인한 것이 아니라면 데이터 유형 자체의 실제 변경(추가, 삭제, 변경)으로 인한 것입니다. 일반적으로 abidiff는 이를 포착하지만 알려진 감지 간격으로 인해 놓친다면 MODVERSIONS 메커니즘에서 이를 포착할 수 있습니다.

예를 들어 커널에서 다음과 같이 변경하면 여러 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 * ) ; ...

변경된 유형을 식별하려면 다음 단계를 따르세요.

  1. 소스 코드(일반적으로 .h 파일)에서 기호의 정의를 찾습니다.

    • 개발자의 커널과 GKI 커널 간의 간단한 기호 차이는 다음 명령어를 실행하여 커밋을 찾습니다.
    git blame
    
    • 삭제된 기호(기호가 트리에서 삭제되고 다른 트리에서도 삭제하려는 경우)의 경우 줄을 삭제한 변경사항을 찾아야 합니다. 줄이 삭제된 트리에서 다음 명령어를 사용합니다.
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
    
  2. 반환된 커밋 목록을 검토하여 변경사항 또는 삭제를 찾습니다. 첫 번째 커밋이 찾고 있는 것일 수 있습니다. 찾고 있는 커밋이 아니라면 커밋을 찾을 때까지 목록을 살펴봅니다.

  3. 변경사항을 식별한 후에는 커널에서 되돌리거나 ACK에 업로드하여 병합되도록 합니다.