Giám sát ABI hạt nhân Android

Bạn có thể sử dụng công cụ Giám sát Giao diện nhị phân (ABI) của ứng dụng, có sẵn 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 biểu diễn ABI từ các tệp nhị phân hạt nhân hiện có (mô-đun vmlinux + GKI). Các biểu diễn ABI này là các tệp .stg và danh sách ký hiệu. Giao diện mà biểu diễn đưa ra 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 biểu diễn.

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

Quá trình

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

  1. Xây dựng hạt nhân và biểu diễn ABI của nó .
  2. Phân tích sự khác biệt ABI giữa bản dựng và tham chiếu .
  3. Cập nhật phần trình bày ABI (nếu được yêu cầu) .
  4. Làm việc với danh sách ký hiệu .

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

Danh sách ký hiệu

KMI không bao gồm tất cả các ký hiệu trong kernel hoặc thậm chí tất cả hơn 30.000 ký hiệu được xuất. Thay vào đó, các ký hiệu có thể được mô-đun của nhà cung cấp sử dụng được liệt kê rõ ràng trong một tập hợp các tệp danh sách ký hiệu được duy trì công khai trong thư mục gốc của cây nhân. Sự kết hợp 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ì ở mức ổn định. Một tệp danh sách ký hiệu ví dụ là abi_gki_aarch64_db845c , tệp này khai báo các ký hiệu cần thiết cho DragonBoard 845c .

Chỉ các ký hiệu được liệt kê trong danh sách ký hiệu cũng như 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 vào danh sách ký hiệu của mình nếu các ký hiệu bạn cần không có. Sau khi các giao diện mới nằm trong danh sách ký hiệu và là một phần của mô tả KMI, chúng sẽ được duy trì ở trạng thái ổn định và không được xóa 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 Android Common Kernel (ACK) có bộ danh sách ký hiệu riêng. Không có nỗ lực nào được thực hiện nhằm cung cấp sự ổn định ABI giữa các nhánh hạt 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 nào phải được theo dõi để đảm bảo độ ổn định. Danh sách ký hiệu chính chứa các ký hiệu được yêu cầu bởi các mô-đun hạt nhân GKI. Các nhà cung cấp phải gửi và cập nhật danh sách ký hiệu bổ sung để đảm bảo rằng các giao diện mà họ dựa vào sẽ duy trì khả năng tương thích ABI. Ví dụ: để xem danh sách 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 nhà cung cấp hoặc thiết bị cụ thể. Danh sách đầy đủ được các công cụ sử dụng là sự kết 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 chi tiết của từng ký hiệu, bao gồm chữ ký hàm và cấu trúc dữ liệu lồng nhau.

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

Bạn có thể tạo danh sách ký hiệu KMI cho thiết bị bằng cách sử dụng hướng dẫn từ Cách làm việc với 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à một yêu cầu khó khăn. Nếu nó giúp ích cho việc bảo trì, bạn có thể gửi nhiều danh sách ký hiệu.

Mở rộng KMI

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

Hạt nhân có các nguồn và các tệp nhị phân được xây dựng từ các nguồn đó. Các nhánh hạt nhân được ABI giám sát bao gồm bản trình bày ABI của GKI ABI hiện tại (ở dạng tệp .stg ). Sau khi các tệp nhị phân ( vmlinux , Image và bất kỳ mô-đun GKI nào) được tạo, một biểu diễn ABI có thể được trích xuất từ ​​các tệp nhị phân. Bất kỳ thay đổi nào được thực hiện đối với tệp nguồn kernel đều có thể ảnh hưởng đến các tệp nhị phân và do đó cũng ảnh hưởng đến .stg được 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ác thành phần lạ của bản dựng và đặt nhãn Lint-1 cho sự thay đổi trong Gerrit nếu nó tìm thấy sự khác biệt về ngữ nghĩa.

Xử lý sự cố ABI

Ví dụ: bản vá sau giới thiệu sự cố 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 bản dựng ABI có áp dụng bản vá này, công cụ sẽ thoát với 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

Sự khác biệt ABI được phát hiện 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 ký hiệu mới từ kernel không có trong bất kỳ 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 ), thì trước tiên bạn cần xác minh rằng nó được xuất bằng EXPORT_SYMBOL_GPL( symbol_name ) rồi cập nhật danh sách ký hiệu và biểu diễn XML ABI. Ví dụ: những thay đổi sau sẽ thêm tính năng FS tăng dần mới vào nhánh android-12-5.10 , bao gồm việc cập nhật danh sách ký hiệu và cách trình bày ABI XML.

  • 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 ABI có trong aosp/1349377 .

Nếu biểu tượng được xuất (do bạn hoặc nó đã được xuất trước đó) nhưng không có trình điều khiển nào khác đang sử dụng nó thì bạn có thể gặp lỗi xây 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ả kernel và ACK của bạn (xem Cập nhật biểu diễn ABI ). Để biết ví dụ về cách cập nhật danh sách ký hiệu và XML ABI trong ACK, hãy tham khảo aosp/1367601 .

Giải quyết sự cố ABI kernel

Bạn có thể xử lý các sự cố ABI của kernel bằng cách cấu trúc lại mã để không thay đổi ABI hoặc cập nhật cách trình bày ABI . Sử dụng biểu đồ sau để xác định cách tiếp cận tốt nhất cho tình huống của bạn.

Biểu đồ dòng chảy vỡ ABI

Hình 1. Độ phân giải đứt gãy ABI

Mã tái cấu trúc để tránh thay đổi ABI

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

  • Tái cấu trúc các thay đổi của trường cấu trúc. Nếu một thay đổi sửa đổi ABI cho 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 CONFIG được sử dụng cho #ifdef bị tắt cho defconfig và gki_defconfig sản xuất. Để biết ví dụ về cách có thể thêm cấu hình gỡ lỗi vào cấu trúc mà không vi phạm ABI, hãy tham khảo bộ bản vá này .

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

Sửa ABI bị hỏng trên Android Gerrit

Nếu bạn không cố ý phá vỡ ABI kernel thì 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. Các nguyên nhân phổ biến nhất gây ra sự cố là do cấu trúc dữ liệu bị thay đổi và biểu tượng CRC liên quan thay đổi hoặc do thay đổi tùy chọn cấu hình dẫn đến bất kỳ điều nào đã nói ở trên. Bắt đầu bằng cách giải quyết các vấn đề được công cụ tìm thấy.

Bạn có thể tái tạo các kết quả ABI cục bộ, xem Xây dựng hạt nhân và biểu diễn ABI của nó .

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

Nếu bạn tải các thay đổi lên nhánh chứa KMI đã cố định hoặc cuối cùng 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 tìm kiếm báo cáo ABI được tạo trong quá trình xây dựng (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 trích xuất và so sánh ABI.

Nếu AbiAnalyzer tìm thấy một báo cáo không trống, nó 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 bản vá nhận được nhãn Lint+1.

Cập nhật ABI hạt nhân

Nếu việc sửa đổi ABI là không thể tránh khỏi thì bạn phải áp dụng các thay đổi mã, biểu diễn ABI và danh sách ký hiệu cho ACK. Để Lint xóa -1 và không phá vỡ tính tương thích của GKI, hãy làm theo các bước sau:

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

  2. Đợi nhận Code-Review +2 cho bản vá.

  3. Cập nhật biểu diễn ABI tham chiếu .

  4. Hợp nhất các thay đổi mã của bạn và thay đổi cập nhật ABI.

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

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

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

    • aosp/1289677 là cần thiết để âm thanh hoạt động.
    • aosp/1295945 là cần thiết để USB hoạt động.
  • Nếu thay đổi ABI dành cho một tính năng có thể được chia sẻ với ACK, thì thay đổi đó có thể được chọn thành ACK như hiện tại. Ví dụ: những thay đổi sau đây không cần thiết cho kiểm tra CTS hoặc VTS nhưng có thể chia sẻ với ACK:

  • Nếu 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 cho ACK bằng cách sử dụng sơ khai như được mô tả trong phần sau.

Sử dụng sơ khai cho ACK

Sơ khai chỉ cần thiết cho những thay đổi lõi lõi không có lợi cho ACK, chẳng hạn như thay đổi hiệu suất và sức mạnh. Danh sách sau đây nêu chi tiết các ví dụ về sơ khai và chọn một phần trong ACK cho GKI.

  • Sơ khai tính năng cô lập lõi ( aosp/1284493 ). Chức năng trong ACK là không cần thiết nhưng các ký hiệu cần phải có trong ACK để mô-đun của bạn sử dụng các ký hiệu này.

  • Biểu tượng 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 ( aosp/1288454 ). Bản vá ban đầu được chọn thành 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 khác biệt 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 viên cuối cùng.

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

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

    • Bản vá aosp/1291018 đã khắc phục các sự cố chức năng được tìm thấy trong quá trình kiểm tra GKI của bản vá trước đó. Bản sửa lỗi bao gồm việc khởi tạo 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.

  • CONFIG_NL80211_TESTMODE Thay đổi ABI ( aosp/1344321 ). Bản vá này đã bổ sung các thay đổi cấu trúc cần thiết 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 sản xuất của họ mà vẫn duy trì tuân thủ GKI.

Thực thi KMI khi chạy

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

Vì mục đích phát triển, bạn có thể sử dụng bản dựng nhân GKI không bao gồm việc cắt bớt ký hiệu (nghĩa là có thể sử dụng tất cả các ký hiệu thường được xuất). Để xác định các bản dựng này, hãy tìm 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 tuân thủ KMI khi chạy. Việc lập phiên bản mô-đun có thể gây ra lỗi không khớp với kiểm tra dự phòng theo chu kỳ (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à lỗi điển hình xảy ra tại thời điểm tải mô-đun do CRC không khớp với ký hiệu 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 ''

Sử dụng phiên bản mô-đun

Lập phiên bản mô-đun rất hữu ích vì những lý do sau:

  • Phiên bản mô-đun nắm bắt những thay đổi về khả năng 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 không rõ ràng, tức là các cấu trúc dữ liệu không phải là một phần của KMI, thì chúng sẽ bị hỏng sau những thay đổi về cấu trúc 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 các giả định về kích thước của nó.

    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 nó nữa. Sau đó, mô-đun có thể thực hiện các thay đổi đối với device->fwnode->dev hoặc device->fwnode->ops . Kịch bản này có vấn đề vì nhiều lý do, được nêu như sau:

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

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

  • Một mô-đun hiện tại được coi là không tương thích với KMI khi nó được tải vào một ngày sau đó bởi một hạt nhân mới không tương thích. Lập phiên bản mô-đun bổ sung kiểm tra thời gian chạy để tránh vô tình tải mô-đun không tương thích với KMI với kernel. Kiểm tra này ngăn chặn các sự cố thời gian chạy khó gỡ lỗi và sự cố hạt nhân có thể xảy ra do sự không tương thích chưa được phát hiện trong KMI.

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

Kiểm tra CRC không khớp mà không cần khởi động thiết bị

stgdiff so sánh và báo cáo sự không khớp CRC giữa các hạt nhân cùng với những khác biệt ABI khác.

Ngoài ra, bản dựng kernel đầy đủ có bật CONFIG_MODVERSIONS sẽ tạo ra tệp Module.symvers như một phần của quy 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 được xuất bởi kernel ( vmlinux ) và các mô-đun. Mỗi dòng bao gồm giá trị CRC, tên ký hiệu, không gian tên ký hiệu, vmlinux hoặc tên mô-đun đang xuất ký hiệu 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 bạn để kiểm tra xem có bất kỳ sự khác biệt CRC nào trong các ký hiệu được xuất bởi vmlinux hay không. Nếu có sự khác biệt về giá trị CRC trong bất kỳ ký hiệu nào được vmlinux xuất ký hiệu đó được sử dụng bởi một trong các mô-đun bạn tải trong thiết bị của mình thì mô-đun đó sẽ không tải.

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

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

Ví dụ: lệnh sau 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 CRC không khớp

Sử dụng các bước sau để giải quyết CRC không khớp khi tải mô-đun:

  1. Xây dựng hạt nhân GKI và hạt nhân thiết bị của bạn bằng tùy chọn --kbuild_symtypes như trong lệnh sau:

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

    Lệnh này tạo tệp .symtypes cho mỗi tệp .o . Xem KBUILD_SYMTYPES trong Kleaf để biết chi tiết.

    Đối với Android 13 trở xuống, hãy xây dựng hạt nhân GKI và hạt nhân thiết bị của bạn bằng cách thêm KBUILD_SYMTYPES=1 vào 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 trong đó ký hiệu 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ó tệp .symtypes tương ứng trong GKI và các tạo phẩm xây dựng nhân thiết bị của bạn. Xác định vị trí 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 dòng có nghĩa là ký hiệu có kiểu dữ liệu [struct|union|enum|etc] . Ví dụ:

      t#bool typedef _Bool bool
      
    • Tiền tố # bị thiếu ở đầ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 hai tập tin và sửa tất cả những khác biệt.

Trường hợp 1: Sự khác biệt do khả năng hiển thị kiểu dữ liệu

Nếu một hạt nhân giữ ký hiệu hoặc kiểu dữ liệu mờ đối với các mô-đun còn hạt nhân kia thì không, 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 UNKNOWN cho một ký hiệu và tệp .symtypes từ hạt nhân khác có chế độ xem mở rộng về ký hiệu 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 kernel của bạn sẽ gây ra CRC không khớp, một trong số đó là dành cho module_layout() :

 #include <linux/fwnode.h>

So sánh module.symtypes cho ký hiệu đó, sẽ thấy những 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ị UNKNOWN và hạt nhân GKI có chế độ xem biểu tượng mở rộng (rất khó xảy ra), thì hãy hợp nhất Hạt nhân chung Android 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ị UNKNOWN , nhưng hạt nhân của bạn có các chi tiết bên trong của ký hiệu do những thay đổi được thực hiện đối với hạt nhân của bạn. Điều này là do một trong các tệp trong hạt nhân của bạn đã thêm #include không có trong hạt nhân GKI.

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

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

Mặt khác, để 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 ký hiệu hoặc kiểu dữ liệu có sự khác biệt này. Ví dụ: chỉnh sửa include/linux/fwnode.h cho struct fwnode_handle .

  2. Thêm đoạn 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 dòng 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 của bạn. Lỗi thời gian xây dựng dẫn đến hiển thị chuỗi tệp tiêu đề #include dẫn đến CRC không khớp 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 những liên kết trong chuỗi #include này là do một thay đổi được thực hiện trong kernel của bạn, thay đổi đó bị thiếu trong kernel GKI.

  5. Xác định thay đổi, hoàn nguyên nó trong kernel của bạn hoặc tải nó lên ACK và hợp nhất nó .

Trường hợp 2: Khác biệt do thay đổi kiểu dữ liệu

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

Ví dụ: việc thực hiện thay đổi sau trong kernel của bạn sẽ gây ra một số CRC không khớp 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à dành cho devm_of_platform_populate() .

Nếu bạn so sánh các tệp .symtypes cho ký hiệu đó, nó có thể trông giống 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 ký hiệu trong mã nguồn (thường ở tệp .h ).

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

  3. Sau khi bạn xác định được thay đổi, hãy hoàn nguyên nó trong kernel của bạn hoặc tải nó lên ACK và hợp nhất nó .