Giám sát ABI của nhân hệ điều hành Android

Bạn có thể sử dụng công cụ giám sát giao diện nhị phân của ứng dụng (ABI), có trong Android 11 trở lên, để ổn định ABI trong nhân của nhân Android. Công cụ này thu thập và so sánh các bản trình bày ABI từ các tệp nhị phân hạt nhân hiện có (mô-đun vmlinux+ GKI). Những bản trình bày ABI này là tệp .stg và danh sách biểu tượng. Giao diện mà nội dung trình bày cung cấp chế độ xem được gọi là Giao diện mô-đun hạt nhân (KMI). Bạn có thể sử dụng công cụ này để theo dõi và giảm thiểu các thay đổi đối với KMI.

Công cụ giám sát ABI được phát triển trong AOSP và sử dụng STG (hoặc libabigail trong Android 13 trở xuống) để tạo và so sánh các bản trình bày.

Trang này mô tả công cụ, quy trình thu thập và phân tích các bản trình bày ABI, cũng như cách sử dụng các bản trình bày đó để mang lại sự ổn định cho ABI trong nhân. Trang này cũng cung cấp thông tin để đóng góp các thay đổi cho nhân Android.

Quy trình

Việc phân tích ABI của hạt nhân cần thực hiện nhiều bước, hầu hết các bước đều có thể được tự động hoá:

  1. Xây dựng hạt nhân và bản trình bày ABI của hạt nhân đó.
  2. Phân tích sự khác biệt về ABI giữa bản dựng và tệp đối chiếu.
  3. Cập nhật nội dung trình bày ABI (nếu cần).
  4. Xử lý danh sách ký hiệu.

Hướng dẫn sau đây áp dụng cho mọi hạt nhân mà bạn có thể tạo bằng cách sử dụng chuỗi công cụ được hỗ trợ (chẳng hạn như chuỗi công cụ Clang tạo sẵn). repo manifests có sẵn cho tất cả các nhánh nhân phổ biến của Android và cho một số nhân dành riêng cho thiết bị, các nhân này đảm bảo rằng bạn sử dụng đúng chuỗi công cụ khi tạo bản phân phối nhân để phân tích.

Danh sách biểu tượng

KMI không bao gồm tất cả các biểu tượng trong nhân hoặc thậm chí là toàn bộ hơn 30.000 biểu tượng được xuất. Thay vào đó, các biểu tượng mà mô-đun của nhà cung cấp có thể sử dụng được liệt kê rõ ràng trong một tập hợp các tệp danh sách biểu tượng được duy trì công khai trong thư mục gốc của cây nhân kernel. Tập hợp hợp nhất của tất cả các ký hiệu trong tất cả các tệp danh sách ký hiệu xác định tập hợp các ký hiệu KMI được duy trì ổn định. Tệp danh sách biểu tượng mẫu là abi_gki_aarch64_db845c, khai báo các biểu tượng bắt buộc cho DragonBoard 845c.

Chỉ những ký hiệu được liệt kê trong danh sách ký hiệu và các cấu trúc và định nghĩa liên quan của chúng mới được coi là một phần của KMI. Bạn có thể đăng các thay đổi lên danh sách ký hiệu nếu không có ký hiệu bạn cần. Sau khi giao diện mới nằm trong danh sách ký hiệu và là một phần của nội dung mô tả KMI, các giao diện này sẽ được duy trì ở trạng thái ổn định và không được xoá khỏi danh sách ký hiệu hoặc sửa đổi sau khi nhánh bị đóng băng.

Mỗi nhánh hạt nhân KMI của Hạt nhân phổ biến (ACK) của Android đều có một bộ danh sách biểu tượng riêng. Không có nỗ lực nào để cung cấp tính ổn định của ABI giữa các nhánh nhân KMI khác nhau. Ví dụ: KMI cho android12-5.10 hoàn toàn độc lập với KMI cho android13-5.10.

Các công cụ ABI sử dụng danh sách ký hiệu KMI để giới hạn những giao diện cần được theo dõi để đảm bảo tính ổn định. Danh sách biểu tượng chính chứa các ký hiệu mà các mô-đun nhân GKI yêu cầu. Nhà cung cấp dự kiến sẽ gửi và cập nhật các danh sách biểu tượng bổ sung để đảm bảo rằng giao diện mà họ dựa vào duy trì khả năng tương thích với ABI. Ví dụ: để xem danh sách các danh sách ký hiệu cho android13-5.15, hãy tham khảo https://android.googlesource.com/kernel/common/+/refs/heads/android13-5.15/android

Danh sách ký hiệu chứa các ký hiệu được báo cáo là cần thiết cho một nhà cung cấp hoặc thiết bị cụ thể. Danh sách đầy đủ mà các công cụ sử dụng là tập hợp của tất cả các tệp danh sách ký hiệu KMI. Các công cụ ABI xác định thông tin chi tiết của từng biểu tượng, bao gồm cả chữ ký hàm và cấu trúc dữ liệu lồng nhau.

Khi KMI bị đóng băng, bạn không được phép thay đổi giao diện KMI hiện có; giao diện này sẽ ổn định. Tuy nhiên, nhà cung cấp có thể tự do thêm các ký hiệu vào KMI bất cứ lúc nào miễn là các nội dung bổ sung không ảnh hưởng đến độ ổn định của ABI hiện có. Các ký hiệu mới thêm sẽ được duy trì ở trạng thái ổn định ngay khi được danh sách ký hiệu KMI trích dẫn. Bạn không nên xoá các ký hiệu khỏi danh sách cho một hạt nhân trừ phi có thể xác nhận rằng không có thiết bị nào từng được vận chuyển với phần phụ thuộc trên ký hiệu đó.

Bạn có thể tạo danh sách ký hiệu KMI cho một thiết bị bằng cách làm theo hướng dẫn trong phần Cách xử lý danh sách ký hiệu. Nhiều đối tác gửi một danh sách ký hiệu cho mỗi ACK, nhưng đây không phải là yêu cầu bắt buộc. Nếu việc này giúp ích cho việc bảo trì, bạn có thể gửi nhiều danh sách biểu tượng.

Mở rộng KMI

Mặc dù các ký hiệu KMI và cấu trúc liên quan được duy trì ở trạng thái ổn định (nghĩa là không thể chấp nhận các thay đổi làm hỏng giao diện ổn định trong một hạt nhân có KMI bị đóng băng), nhưng hạt nhân GKI vẫn mở cho các tiện ích để các thiết bị xuất xưởng vào cuối năm không cần xác định tất cả các phần phụ thuộc trước khi KMI bị đóng băng. Để mở rộng KMI, bạn có thể thêm các ký hiệu mới vào KMI cho các hàm nhân mới hoặc hiện có được xuất, ngay cả khi KMI bị đóng băng. Các bản vá nhân hệ điều hành mới cũng có thể được chấp nhận nếu chúng không phá vỡ KMI.

Giới thiệu về lỗi KMI

Hạt nhân có các nguồn và tệp nhị phân được xây dựng từ các nguồn đó. Các nhánh hạt nhân được giám sát ABI bao gồm một ABI đại diện cho ABI GKI hiện tại (ở dạng tệp .stg). Sau khi tạo các tệp nhị phân (vmlinux, Image và mọi mô-đun GKI), bạn có thể trích xuất một bản trình bày ABI từ các tệp nhị phân. Mọi thay đổi đối với tệp nguồn nhân hệ điều hành đều có thể ảnh hưởng đến các tệp nhị phân và sau đó cũng ảnh hưởng đến .stg đã trích xuất. Trình phân tích AbiAnalyzer so sánh tệp .stg đã cam kết với tệp được trích xuất từ cấu phần phần mềm bản dựng và đặt nhãn Lint-1 trên thay đổi trong Gerrit nếu tìm thấy sự khác biệt về ngữ nghĩa.

Xử lý lỗi ABI

Ví dụ: bản vá sau đây gây ra lỗi ABI rất rõ ràng:

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.

Khi bạn chạy ABI bản dựng bằng bản vá này, công cụ sẽ thoát bằng một mã lỗi khác 0 và báo cáo sự khác biệt về ABI tương tự như sau:

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

Phát hiện sự khác biệt về ABI tại thời điểm xây dựng

Lý do phổ biến nhất gây ra lỗi là khi trình điều khiển sử dụng một ký hiệu mới từ hạt nhân không có trong danh sách ký hiệu nào.

Nếu ký hiệu không có trong danh sách ký hiệu (android/abi_gki_aarch64), trước tiên, bạn cần xác minh rằng ký hiệu đó được xuất bằng EXPORT_SYMBOL_GPL(symbol_name), sau đó cập nhật phần trình bày XML ABI và danh sách ký hiệu. Ví dụ: các thay đổi sau đây sẽ thêm tính năng FS gia tăng mới vào nhánh android-12-5.10, bao gồm cả việc cập nhật danh sách biểu tượng và nội dung trình bày XML ABI.

  • Ví dụ về thay đổi tính năng có trong aosp/1345659.
  • Ví dụ về danh sách biểu tượng có trong aosp/1346742.
  • Ví dụ về thay đổi XML cho ABI nằm trong aosp/1349377.

Nếu biểu tượng này đã được xuất (do bạn hoặc trước đó đã được xuất) nhưng không có trình điều khiển nào khác đang sử dụng biểu tượng đó, thì bạn có thể gặp lỗi bản dựng tương tự như sau.

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

Để giải quyết, hãy cập nhật danh sách ký hiệu KMI trong cả hạt nhân và ACK (xem phần Cập nhật nội dung trình bày ABI). Để biết ví dụ về cách cập nhật XML ABI và danh sách biểu tượng trong ACK, hãy tham khảo aosp/1367601.

Giải quyết lỗi ABI của hạt nhân

Bạn có thể xử lý các lỗi ABI của hạt nhân bằng cách tái cấu trúc mã để không thay đổi ABI hoặc cập nhật cách trình bày ABI. Hãy sử dụng biểu đồ sau đây để xác định phương pháp phù hợp nhất với trường hợp của bạn.

Sơ đồ quy trình xử lý lỗi ABI

Hình 1. Giải pháp khắc phục lỗi ABI

Tái cấu trúc mã để tránh thay đổi ABI

Cố gắng hết sức để tránh sửa đổi ABI hiện có. Trong nhiều trường hợp, bạn có thể tái cấu trúc mã để xoá những thay đổi ảnh hưởng đến ABI.

  • Tái cấu trúc các thay đổi về trường cấu trúc. Nếu có một thay đổi sửa đổi ABI cho một tính năng gỡ lỗi, hãy thêm #ifdef xung quanh các trường (trong cấu trúc và tham chiếu nguồn) và đảm bảo rằng CONFIG dùng cho #ifdef đã bị tắt cho defconfig bản chính thức và gki_defconfig. Để biết ví dụ về cách thêm cấu hình gỡ lỗi vào một cấu trúc mà không làm hỏng ABI, hãy tham khảo nhóm bản vá này.

  • Tái cấu trúc các tính năng để không thay đổi hạt nhân cốt lõi. Nếu cần thêm các tính năng mới vào ACK để hỗ trợ các mô-đun đối tác, hãy cố gắng tái cấu trúc phần ABI của thay đổi để tránh sửa đổi ABI hạt nhân. Để biết ví dụ về cách sử dụng ABI hạt nhân hiện có để thêm các chức năng bổ sung mà không thay đổi ABI hạt nhân, hãy tham khảo aosp/1312213.

Khắc phục ABI bị hỏng trên Android Gerrit

Nếu không cố ý phá vỡ ABI hạt nhân, bạn cần điều tra bằng cách sử dụng hướng dẫn do công cụ giám sát ABI cung cấp. Nguyên nhân phổ biến nhất dẫn đến lỗi là do thay đổi cấu trúc dữ liệu và các thay đổi CRC biểu tượng liên quan, hoặc do thay đổi tuỳ chọn cấu hình dẫn đến bất kỳ nguyên nhân nào nêu trên. Hãy bắt đầu bằng cách giải quyết các vấn đề mà công cụ này phát hiện được.

Bạn có thể tái tạo các phát hiện về ABI trên máy, hãy xem phần Tạo hạt nhân và cách trình bày ABI của hạt nhân.

Giới thiệu về nhãn Lint-1

Nếu bạn tải các thay đổi lên một nhánh chứa KMI đã đóng băng hoặc đã hoàn tất, thì các thay đổi đó phải vượt qua AbiAnalyzer để đảm bảo các thay đổi không ảnh hưởng đến ABI ổn định theo cách không tương thích. Trong quá trình này, AbiAnalyzer sẽ tìm báo cáo ABI được tạo trong quá trình tạo bản dựng (một bản dựng mở rộng thực hiện bản dựng thông thường, sau đó là một số bước so sánh và trích xuất ABI.

Nếu AbiAnalyzer tìm thấy một báo cáo không trống, thì báo cáo này sẽ đặt nhãn Lint-1 và thay đổi sẽ bị chặn gửi cho đến khi được giải quyết; cho đến khi nhóm bản vá nhận được nhãn Lint+1.

Cập nhật ABI kernel

Nếu không thể tránh việc sửa đổi ABI, thì bạn phải áp dụng các thay đổi về mã, nội dung trình bày ABI và danh sách biểu tượng cho ACK. Để yêu cầu Tìm lỗi mã nguồn xoá -1 mà không làm hỏng khả năng tương thích với GKI, hãy làm theo các bước sau:

  1. Tải các thay đổi về mã lên ACK.

  2. Chờ nhận được điểm Code-Review +2 cho nhóm bản vá.

  3. Cập nhật nội dung trình bày ABI tham chiếu.

  4. Hợp nhất các thay đổi về mã và thay đổi về nội dung cập nhật ABI.

Tải các thay đổi về mã ABI lên ACK

Việc cập nhật ABI ACK phụ thuộc vào loại thay đổi đang được thực hiện.

  • Nếu thay đổi ABI liên quan đến một tính năng ảnh hưởng đến kiểm thử CTS hoặc VTS, thì thay đổi đó thường có thể được chọn để ACK như hiện tại. Ví dụ:

  • Nếu thay đổi về ABI dành cho một tính năng có thể chia sẻ được với ACK, thì thay đổi đó có thể được chọn ngay cho ACK. Ví dụ: đối với thử nghiệm CTS hoặc VTS, bạn không cần thay đổi những thay đổi sau đây:

  • Nếu một thay đổi ABI giới thiệu một tính năng mới không cần đưa vào ACK, bạn có thể giới thiệu các ký hiệu vào ACK bằng cách sử dụng một mã giả như mô tả trong phần sau.

Sử dụng mã giả lập cho ACK

Các mã giả lập chỉ được dùng cho những thay đổi về hạt nhân cốt lõi không mang lại lợi ích cho ACK, chẳng hạn như những thay đổi về hiệu suất và nguồn điện. Danh sách sau đây trình bày chi tiết các ví dụ về mã giả và một số lựa chọn trong ACK cho GKI.

  • Mã giả lập tính năng tách biệt lõi (aosp/1284493). Các chức năng trong ACK không cần thiết, nhưng các biểu tượng cần có trong ACK để các mô-đun của bạn có thể sử dụng các biểu tượng này.

  • Biểu tượng phần giữ chỗ cho mô-đun nhà cung cấp (aosp/1288860).

  • Tính năng theo dõi sự kiện mm trên mỗi quy trình chỉ dành cho ABI (Giao diện nhị phân ứng dụng) (aosp/1288454). Bản vá ban đầu được chọn để ACK và sau đó được cắt bớt để chỉ bao gồm những thay đổi cần thiết nhằm giải quyết sự khác biệt về ABI cho task_structmm_event_count. Bản vá này cũng cập nhật enum mm_event_type để chứa các thành phần cuối cùng.

  • Lựa chọn một phần các thay đổi về cấu trúc nhiệt của ABI đòi hỏi nhiều hơn ngoài việc chỉ thêm các trường ABI mới.

    • Bản vá aosp/1255544 đã giải quyết các khác biệt về ABI giữa nhân đối tác và ACK.

    • Bản vá aosp/1291018 đã khắc phục các vấn đề về chức năng phát hiện thấy trong quá trình kiểm thử GKI của bản vá trước đó. Bản sửa lỗi bao gồm việc khởi chạy cấu trúc tham số cảm biến để đăng ký nhiều vùng nhiệt cho một cảm biến.

  • Thay đổi ABI CONFIG_NL80211_TESTMODE (aosp/1344321). Bản vá này đã thêm những thay đổi cần thiết về cấu trúc cho ABI và đảm bảo các trường bổ sung không gây ra sự khác biệt về chức năng, cho phép các đối tác đưa CONFIG_NL80211_TESTMODE vào nhân hệ điều hành phát hành chính thức mà vẫn duy trì tuân thủ GKI.

Thực thi KMI trong thời gian chạy

Hạt nhân GKI sử dụng các tuỳ chọn cấu hình TRIM_UNUSED_KSYMS=yUNUSED_KSYMS_WHITELIST=<union of all symbol lists>, giúp giới hạn các biểu tượng đã xuất (chẳng hạn như các biểu tượng đã xuất bằng EXPORT_SYMBOL_GPL()) ở những biểu tượng được liệt kê trên danh sách biểu tượng. Tất cả các biểu tượng khác đều chưa được xuất và việc tải một mô-đun yêu cầu biểu tượng chưa xuất sẽ bị từ chối. Quy định hạn chế này được thực thi trong thời gian xây dựng và các mục bị thiếu sẽ được gắn cờ.

Đối với mục đích phát triển, bạn có thể sử dụng một bản dựng hạt nhân GKI không bao gồm việc cắt biểu tượng (nghĩa là có thể sử dụng tất cả các biểu tượng thường được xuất). Để tìm các bản dựng này, hãy tìm các bản dựng kernel_debug_aarch64 trên ci.android.com.

Thực thi KMI bằng cách sử dụng phiên bản mô-đun

Hạt nhân Hình ảnh hạt nhân chung (GKI) sử dụng phiên bản mô-đun (CONFIG_MODVERSIONS) làm biện pháp bổ sung để thực thi việc tuân thủ KMI trong thời gian chạy. Việc tạo phiên bản mô-đun có thể gây ra lỗi không khớp kiểm tra dư thừa tuần hoàn (CRC) tại thời điểm tải mô-đun nếu KMI dự kiến của mô-đun không khớp với KMI vmlinux. Ví dụ sau đây là một lỗi thông thường xảy ra tại thời điểm tải mô-đun do CRC không khớp đối với biểu tượng 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 ''

Việc sử dụng phiên bản mô-đun

Việc tạo phiên bản mô-đun rất hữu ích vì những lý do sau:

  • Việc lập phiên bản mô-đun sẽ nắm bắt các thay đổi về chế độ hiển thị cấu trúc dữ liệu. Nếu các mô-đun thay đổi cấu trúc dữ liệu mờ, tức là các cấu trúc dữ liệu không thuộc KMI, thì các mô-đun đó sẽ bị lỗi sau khi cấu trúc thay đổi trong tương lai.

    Ví dụ: hãy xem xét trường fwnode trong struct device. Trường này PHẢI mờ đối với các mô-đun để chúng không thể thay đổi các trường của device->fw_node hoặc đưa ra giả định về kích thước của trường.

    Tuy nhiên, nếu một mô-đun bao gồm <linux/fwnode.h> (trực tiếp hoặc gián tiếp), thì trường fwnode trong struct device không còn mờ đối với mô-đun đó nữa. Sau đó, mô-đun này có thể thực hiện các thay đổi đối với device->fwnode->dev hoặc device->fwnode->ops. Tình huống này có vấn đề vì một số lý do, được nêu như sau:

    • Nó có thể phá vỡ các giả định mà mã hạt nhân cốt lõi đang đưa ra về cấu trúc dữ liệu nội bộ của nó.

    • Nếu bản cập nhật nhân trong tương lai thay đổi struct fwnode_handle (loại dữ liệu của fwnode), thì mô-đun sẽ không còn hoạt động với nhân mới. Hơn nữa, stgdiff sẽ không cho thấy sự khác biệt nào vì mô-đun này đang phá vỡ KMI bằng cách trực tiếp thao tác với các cấu trúc dữ liệu nội bộ theo những cách không thể ghi lại được bằng cách chỉ kiểm tra bản trình bày nhị phân.

  • Một mô-đun hiện tại được coi là không tương thích với KMI khi mô-đun đó được tải vào một ngày sau đó bằng một hạt nhân mới không tương thích. Việc tạo phiên bản mô-đun sẽ thêm một bước kiểm tra thời gian chạy để tránh vô tình tải một mô-đun không tương thích với KMI với hạt nhân. Bước kiểm tra này ngăn chặn các vấn đề khó gỡ lỗi trong thời gian chạy và sự cố hạt nhân có thể dẫn đến tình trạng không tương thích không được phát hiện trong KMI.

Việc bật tính năng tạo phiên bản mô-đun sẽ ngăn chặn tất cả các vấn đề này.

Kiểm tra để tìm những trường hợp không khớp CRC mà không cần khởi động thiết bị

stgdiff so sánh và báo cáo các trường hợp không khớp CRC giữa các hạt nhân cùng với các điểm khác biệt khác về ABI.

Ngoài ra, bản dựng hạt nhân đầy đủ với CONFIG_MODVERSIONS được bật sẽ tạo một tệp Module.symvers trong quá trình xây dựng thông thường. Tệp này có một dòng cho mỗi ký hiệu do hạt nhân (vmlinux) và các mô-đun xuất. Mỗi dòng bao gồm giá trị CRC, tên biểu tượng, không gian tên biểu tượng, vmlinux hoặc tên mô-đun đang xuất biểu tượng và loại xuất (ví dụ: EXPORT_SYMBOL so với EXPORT_SYMBOL_GPL).

Bạn có thể so sánh các tệp Module.symvers giữa bản dựng GKI và bản dựng của mình để kiểm tra xem có sự khác biệt nào về CRC trong các ký hiệu do vmlinux xuất ra hay không. Nếu có sự khác biệt về giá trị CRC trong bất kỳ biểu tượng nào do vmlinux xuất biểu tượng đó được một trong các mô-đun bạn tải trong thiết bị sử dụng, thì mô-đun đó sẽ không tải.

Nếu không có tất cả cấu phần phần mềm xây dựng, nhưng có các tệp vmlinux của hạt nhân GKI và hạt nhân của bạn, bạn có thể so sánh các giá trị CRC cho một biểu tượng cụ thể bằng cách chạy lệnh sau trên cả hai hạt nhân và so sánh đầu ra:

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

Ví dụ: lệnh sau đây kiểm tra giá trị CRC cho ký hiệu module_layout:

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

Giải quyết trường hợp không khớp CRC

Hãy làm theo các bước sau để giải quyết lỗi không khớp CRC khi tải mô-đun:

  1. Tạo nhân GKI và nhân của thiết bị bằng cách sử dụng tuỳ chọn --kbuild_symtypes như minh hoạ trong lệnh sau:

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

    Lệnh này tạo một tệp .symtypes cho mỗi tệp .o. Hãy xem KBUILD_SYMTYPES trong Kleaf để biết thông tin chi tiết.

    Đối với Android 13 trở xuống, hãy tạo hạt nhân GKI và hạt nhân thiết bị bằng cách thêm KBUILD_SYMTYPES=1 vào đầu lệnh bạn sử dụng để tạo hạt nhân, như trong lệnh sau:

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

    Khi sử dụng build_abi.sh,, cờ KBUILD_SYMTYPES=1 đã được đặt ngầm.

  2. Tìm tệp .c chứa biểu tượng có CRC không khớp được xuất bằng lệnh sau:

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
    
  3. Tệp .c có một tệp .symtypes tương ứng trong GKI và các cấu phần phần mềm bản dựng hạt nhân của thiết bị. Tìm tệp .c bằng các lệnh sau:

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

    Sau đây là các đặc điểm của tệp .c:

    • Định dạng của tệp .c là một dòng (có thể rất dài) cho mỗi ký hiệu.

    • [s|u|e|etc]# ở đầu đường có nghĩa là biểu tượng thuộc loại dữ liệu [struct|union|enum|etc]. Ví dụ:

      t#bool typedef _Bool bool
      
    • Việc thiếu tiền tố # ở đầu dòng cho biết ký hiệu đó là một hàm. Ví dụ:

      find_module s#module * find_module ( const char * )
      
  4. So sánh 2 tệp rồi khắc phục tất cả các điểm khác biệt.

Trường hợp 1: Sự khác biệt do chế độ hiển thị loại dữ liệu

Nếu một hạt nhân giữ một biểu tượng hoặc loại dữ liệu không rõ ràng đối với các mô-đun và hạt nhân khác không làm như vậy, thì sự khác biệt đó sẽ xuất hiện giữa các tệp .symtypes của hai hạt nhân. Tệp .symtypes từ một trong các hạt nhân có UNKNOWN cho một biểu tượng và tệp .symtypes từ hạt nhân còn lại có chế độ xem mở rộng của biểu tượng hoặc loại dữ liệu.

Ví dụ: việc thêm dòng sau vào tệp include/linux/device.h trong hạt nhân sẽ gây ra sự không khớp CRC, một trong số đó là module_layout():

 #include <linux/fwnode.h>

Khi so sánh module.symtypes cho biểu tượng đó, bạn sẽ thấy các điểm khác biệt sau:

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

Nếu hạt nhân của bạn có giá trị là UNKNOWN và hạt nhân GKI có chế độ xem mở rộng của biểu tượng (rất khó xảy ra), hãy hợp nhất Hạt nhân Android phổ biến mới nhất vào hạt nhân của bạn để bạn đang sử dụng cơ sở hạt nhân GKI mới nhất.

Trong hầu hết các trường hợp, hạt nhân GKI có giá trị là UNKNOWN, nhưng hạt nhân của bạn có thông tin chi tiết nội bộ về biểu tượng do các thay đổi đã thực hiện đối với hạt nhân. Lý do là một trong các tệp trong hạt nhân của bạn đã thêm một #include không có trong hạt nhân GKI.

Thông thường, cách khắc phục chỉ là ẩn #include mới khỏi genksyms.

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

Nếu không, để xác định #include gây ra sự khác biệt, hãy làm theo các bước sau:

  1. Mở tệp tiêu đề xác định biểu tượng hoặc kiểu dữ liệu có điểm khác biệt này. Ví dụ: chỉnh sửa include/linux/fwnode.h cho struct fwnode_handle.

  2. Thêm mã sau vào đầu tệp tiêu đề:

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. Trong tệp .c của mô-đun có CRC không khớp, hãy thêm nội dung sau làm dòng đầu tiên trước bất kỳ dòng #include nào.

    #define CRC_CATCH 1
    
  4. Biên dịch mô-đun. Lỗi thời gian tạo kết quả cho thấy chuỗi tệp tiêu đề #include dẫn đến sự không khớp CRC này. Ví dụ:

    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"
    

    Một trong các đường liên kết trong chuỗi #include này là do một thay đổi đã được thực hiện trong hạt nhân của bạn, nhưng lại bị thiếu trong hạt nhân GKI.

  5. Xác định thay đổi, huỷ bỏ thay đổi đó trong hạt nhân hoặc tải thay đổi đó lên ACK và hợp nhất thay đổi đó.

Trường hợp 2: Điểm khác biệt do các thay đổi về loại dữ liệu

Nếu CRC không khớp đối với một biểu tượng hoặc loại dữ liệu không phải là do sự khác biệt về mức độ hiển thị, thì đó là do những thay đổi thực tế (thêm, xoá hoặc thay đổi) trong chính loại dữ liệu đó.

Ví dụ: việc thực hiện thay đổi sau đây trong hạt nhân sẽ gây ra một số lỗi không khớp CRC vì nhiều ký hiệu bị ảnh hưởng gián tiếp bởi loại thay đổi này:

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

Một CRC không khớp là đối với devm_of_platform_populate().

Nếu so sánh các tệp .symtypes cho biểu tượng đó, bạn có thể thấy như sau:

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

Để xác định loại đã thay đổi, hãy làm theo các bước sau:

  1. Tìm định nghĩa của biểu tượng trong mã nguồn (thường có trong các tệp .h).

    • Đối với sự khác biệt về biểu tượng giữa hạt nhân của bạn và hạt nhân GKI, hãy tìm thay đổi bằng cách chạy lệnh sau:
    git blame
    
    • Đối với các ký hiệu đã xoá (trong đó một ký hiệu bị xoá trong một cây và bạn cũng muốn xoá ký hiệu đó trong cây khác), bạn cần tìm thay đổi đã xoá dòng đó. Hãy sử dụng lệnh sau trên cây đã xoá dòng:
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
    
  2. Xem lại danh sách cam kết được trả về để xác định điểm thay đổi hoặc xoá. Lần thay đổi đầu tiên có thể là lần thay đổi bạn đang tìm kiếm. Nếu không, hãy xem danh sách cho đến khi bạn tìm thấy thay đổi đó.

  3. Sau khi xác định thay đổi, hãy huỷ thay đổi đó trong hạt nhân hoặc tải thay đổi đó lên ACK và hợp nhất thay đổi đó.