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 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). Các nội dung đại diện ABI này là tệp .stg và danh sách biểu tượng. Giao diện bật mà bản trình bày cung cấp một khung hiển thị được gọi là Giao diện mô-đun Kernel (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 ABI và việc sử dụng các bản trình bày đó để đảm bảo tính ổn định cho ABI trong nhân. Trang này cũng cung cấp thông tin về những thay đổi góp phần cho nhân Android.

Quy trình

Quá trình phân tích ABI của nhân hệ điều hành bao gồm nhiều bước, hầu hết các bước đều đượ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 tham 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.

Các hướng dẫn sau đây áp dụng cho bất kỳ hạt nhân mà bạn có thể xây dựng bằng cách sử dụng được hỗ trợ (chẳng hạn như chuỗi công cụ Clang được tạo sẵn). repo manifests có sẵn cho tất cả các nhánh nhân hệ điều hành phổ biến của Android và cho một số nhân dành riêng cho thiết bị, giúp đảm bảo bạn sử dụng đúng chuỗi công cụ khi bạn xây dựng 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 ký hiệu trong nhân hoặc thậm chí là tất cả hơn 30.000 ký hiệu ký hiệu đã xuất. Thay vào đó, các ký hiệu mà mô-đun nhà cung cấp có thể sử dụng là đượ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 hạt nhân. 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 cần thiết 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 thay đổi lên nếu không có các ký hiệu bạn cần. Sau khi có giao diện mới danh sách biểu tượng và là một phần của mô tả KMI, chúng được duy trì ở mức ổn định và không được xoá khỏi danh sách biểu tượng hoặc sửa đổi sau khi nhánh được bị treo.

Mỗi nhánh hạt nhân KMI của Hạt nhân phổ biến (ACK) của Android có một bộ biểu tượng riêng danh sách. Không có nỗ lực nào để cung cấp tính ổn định cho ABI giữa các nhân KMI khác nhau cành cây. 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 biểu tượng KMI để giới hạn những giao diện phải được giám sát độ ổn định. Chiến lược phát hành đĩa đơn danh sách biểu tượng chính chứa các ký hiệu mà 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ổng hợp tất cả Tệp danh sách biểu tượng 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ó. Mới thêm các biểu tượng được duy trì ổn định ngay sau khi chúng được trích dẫn bởi một danh sách biểu tượng KMI. 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 biểu tượng 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 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 biểu tượng mới vào KMI cho các hàm nhân hệ điều hành đã xuất hiện có, ngay cả khi KMI bị treo. Các bản vá nhân mới cũng có thể được chấp nhận nếu không làm hỏng KMI.

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

Hạt nhân có nguồn và các tệp nhị phân được tạo từ các nguồn đó. Các nhánh nhân được giám sát theo ABI chứa một bản đại diện ABI của GKI hiện tại ABI (ở dạng tệp .stg). Sau tệp nhị phân (vmlinux, Image và bất kỳ mô-đun GKI nào), thì bản trình bày ABI có thể được trích xuất từ tệp nhị phân. Mọi thay đổi đối với tệp nguồn kernel đều có thể ảnh hưởng đến tệp nhị phân và 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 đã xác nhận, trong đó tệp được trích xuất từ các cấu phần phần mềm bản dựng và đặt một Nhãn tìm lỗi mã nguồn 1 trên sự 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 biểu tượng đó không có trong danh sách biểu tượng (android/abi_gki_aarch64), thì trước tiên, bạn cần xác minh rằng dữ liệu được xuất bằng EXPORT_SYMBOL_GPL(symbol_name), sau đó cập nhật Danh sách biểu tượng và bản trình bày XML của ABI. Ví dụ: những 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 việc cập nhật danh sách biểu tượng và bản trình bày XML của ABI.

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

Nếu biểu tượng được xuất (do bạn hoặc đã được xuất trước đó) nhưng không trình điều khiển khác đang sử dụng đoạn mã đó, 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 biểu tượng KMI trong cả nhân và ACK (xem Cập nhật bản 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. Sử dụng biểu đồ để xác định phương pháp tốt nhất cho trường hợp của bạn.

Biểu đồ quy trình ngắt ABI

Hình 1. Độ phân giải của ABI (Giao diện nhị phân ứng dụng)

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

Hãy 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á các 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 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à tệp tham chiếu nguồn) và đảm bảo CONFIG dùng cho #ifdef bị tắt cho defconfig sản xuất và gki_defconfig. Ví dụ về cách gỡ lỗi cấu hình có thể được thêm vào một cấu trúc mà không làm hỏng ABI, hãy tham khảo bài viết này patchset.

  • Tái cấu trúc các tính năng để không thay đổi nhân hệ điều hành chính. 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. Ví dụ về cách sử dụng ABI nhân hiện tại để thêm các tính năng bổ sung mà không cần thay đổi ABI nhân tham chiếu đến aosp/1312213.

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

Nếu không cố ý phá vỡ ABI nhân hệ điều hành thì bạn cần điều tra, theo hướng dẫn do công cụ theo dõi ABI cung cấp. Phổ biến nhất nguyên nhân gây ra sự cố là cấu trúc dữ liệu bị thay đổi và ký hiệu tương ứng là CRC hoặc do các thay đổi về tuỳ chọn cấu hình dẫn đến bất kỳ sự thay đổi 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à nội dung đại diện 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 kiếm Báo cáo ABI được tạo trong quá trình tạo bản dựng (bản dựng mở rộng thực hiện bản dựng thông thường và sau đó là một số bước so sánh và trích xuất ABI.

Nếu tìm thấy một báo cáo không trống, AbiAnalyzer sẽ đặt nhãn Tìm lỗi mã nguồn 1 và thay đổi sẽ không được gửi cho đến khi được giải quyết; cho đến khi tập hợp bản vá nhận được Nhãn tìm lỗi mã nguồn+1.

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

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 và 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 2 tính năng Đánh giá mã cho bộ 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 tuỳ thuộc vào loại thay đổi đượ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 xảy ra với một tính năng có thể chia sẻ được với ACK, thì thay đổi có thể được chọn chính xác cho ACK. Ví dụ: những thay đổi sau không cần cho xét nghiệm CTS hoặc VTS nhưng có thể chia sẻ với ACK:

  • 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 đoạn mã tạm thời chỉ cần thiết cho những thay đổi về nhân cốt lõi không mang lại lợi ích cho ACK, chẳng hạn như thay đổi về hiệu suất và nguồ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 cố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).

  • Chỉ ABI mới có thể chọn tính năng theo dõi sự kiện mm theo quy trình (aosp/1288454). Bản vá ban đầu được chọn tại ACK, sau đó được cắt bớt để chỉ đưa vào bản vá các thay đổi cần thiết để giải quyết điểm 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.

  • Một số thay đổi ABI cấu trúc nhiệt cần nhiều hơn 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.

    • Miếng dán 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.

  • CONFIG_NL80211_TESTMODE thay đổi về ABI (aosp/1344321). Bản vá này thêm các 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, nên các đối tác có thể để đưa CONFIG_NL80211_TESTMODE vào hạt nhân sản xuất 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 được xuất sử dụng EXPORT_SYMBOL_GPL()) sang các 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 chưa được xuất và việc tải một mô-đun đòi hỏi biểu tượng chưa xuất bị từ chối. Quy định hạn chế này được thực thi tại thời điểm xây dựng và mục nhập bị thiếu đã bị gắn cờ.

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

Thực thi KMI bằng cách sử dụng tính năng tạo 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 tại thời gian chạy. Việc tạo phiên bản mô-đun có thể khiến tính năng kiểm tra dự phòng theo chu kỳ (CRC) không khớp lỗi tại thời gian tải mô-đun nếu KMI dự kiến của một mô-đun không khớp với vmlinux KMI. 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 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 hữu ích vì những lý do sau:

  • Việc tạo phiên bản mô-đun sẽ phát hiện 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ờ (opaque data structure), 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ì 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 được làm mờ đối với các mô-đun để chúng không thể thực hiện các thay đổi đối với trường device->fw_node hoặc đưa ra 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 sẽ không còn mờ đối với mô-đun đó. Chiến lược phát hành đĩa đơn sau đó 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, như sau:

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

    • 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 bị coi là không tương thích với KMI khi được tải vào một ngày sau này bởi mộ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à các sự cố về nhân hệ điều hành có thể do sự không tương thích không xác định được trong KMI.

Việc bật tính năng tạo phiên bản mô-đun sẽ ngă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 trường hợp không khớp CRC giữa các hạt nhân cùng với các hạt nhân khác Sự khác biệt về ABI.

Ngoài ra, bản dựng nhân hệ điều hành đầy đủ có bật CONFIG_MODVERSIONS sẽ tạo ra một Module.symvers trong quy trình xây dựng thông thường. Tệp này có một dòng cho mỗi biểu tượng mà 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 biểu tượng mà vmlinux xuất 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 bạn không có tất cả cấu phần phần mềm của bản dựng nhưng lại có tệp vmlinux của nhân GKI và nhân của bạn, bạn có thể so sánh các giá trị CRC cho một bằng cách chạy lệnh sau trên cả hai 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 module_layout ký hiệu:

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

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

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

  1. Tạo nhân hệ điều hành GKI và nhân hệ điều hành của thiết bị bằng --kbuild_symtypes như được thể hiện 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. 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 nhân GKI và nhân của thiết bị bằng cách thêm KBUILD_SYMTYPES=1 vào lệnh mà bạn dùng sử dụng để tạo nhân, như minh hoạ 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 đó biểu tượng không khớp CRC được xuất, sử dụ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ấu phần phần mềm bản dựng nhân hệ điều hành của thiết bị. Xác định vị trí tệp .c bằng cách sử dụng đoạn mã sau các lệnh:

    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 biểu tượng.

    • [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 rằng 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 cho các mô-đun và hạt nhân khác không giữ, 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 kiểu 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 này có giá trị chi tiết bên trong của biểu tượng do các thay đổi đối với nhân của bạn. Đây là vì một trong các tệp trong nhân của bạn đã thêm một #include không có trong 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 các bước:

  1. Mở tệp tiêu đề xác định biểu tượng hoặc kiểu dữ liệu có hàm này khác biệt. 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 tiếp theo là dòng đầu tiên trước mọi dòng #include.

    #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 có sự thay đổi trong nhân hệ điều hành, bị thiếu trong 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: Sự khác biệt do thay đổi về loại dữ liệu

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

Ví dụ: việc thực hiện thay đổi sau đây trong nhân hệ điều hành 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à đối với devm_of_platform_populate().

Nếu bạn so sánh các tệp .symtypes cho biểu tượng đó, thì mã sẽ có dạ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 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 biểu tượng bị xoá (trong đó một biểu tượng bị xoá trong cây và bạn cũng muốn xoá trong cây khác), bạn cần tìm thay đổi mà đã xoá đường này. Sử dụng lệnh sau trên cây nơi dòng đã bị xoá:
    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ề để tìm nội dung thay đổi hoặc xoá. Chiến lược phát hành đĩa đơn đầu tiên cam kết có thể là điều bạn đang tìm kiếm. Nếu không, hãy truy cập qua danh sách cho đến khi bạn tìm thấy cam kết.

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