ความเสถียรของ ABI

ความเสถียรของอินเทอร์เฟซแบบไบนารีของแอปพลิเคชัน (ABI) เป็นข้อกำหนดเบื้องต้นของการอัปเดตเฉพาะเฟรมเวิร์กเท่านั้น เนื่องจากโมดูลของผู้ให้บริการอาจขึ้นอยู่กับไลบรารีที่ใช้ร่วมกันของผู้ให้บริการแบบเนทีฟ (VNDK) ที่อยู่ในพาร์ติชันระบบ ภายในรุ่น Android ไลบรารี VNDK ที่แชร์ซึ่งสร้างขึ้นใหม่ต้องเข้ากันได้กับ ABI ของไลบรารี VNDK ที่เผยแพร่ก่อนหน้านี้เพื่อให้โมดูลของผู้ให้บริการทำงานร่วมกับไลบรารีเหล่านั้นได้โดยไม่ต้องคอมไพล์ใหม่และไม่มีข้อผิดพลาดรันไทม์ ไลบรารี VNDK อาจเปลี่ยนแปลงได้ระหว่างรุ่นของ Android และไม่มีการรับประกัน ABI

Android 9 มีเครื่องมือตรวจสอบ ABI ของส่วนหัวตามที่อธิบายไว้ในส่วนต่อไปนี้ เพื่อช่วยตรวจสอบความเข้ากันได้ของ ABI

เกี่ยวกับการปฏิบัติตามข้อกำหนดของ VNDK และ ABI

VNDK คือชุดไลบรารีแบบจำกัดที่โมดูลของผู้ให้บริการอาจลิงก์ถึง และเปิดใช้การอัปเดตเฟรมเวิร์กเท่านั้น การปฏิบัติตามข้อกำหนด ABI หมายถึงความสามารถของไลบรารีที่ใช้ร่วมกันเวอร์ชันใหม่ในการทำงานตามที่คาดไว้กับโมดูลที่ลิงก์กับไลบรารีดังกล่าวแบบไดนามิก (กล่าวคือ ทำงานเหมือนกับไลบรารีเวอร์ชันเก่า)

เกี่ยวกับสัญลักษณ์ที่ส่งออก

สัญลักษณ์ที่ส่งออก (หรือที่เรียกว่าสัญลักษณ์ส่วนกลาง) หมายถึงสัญลักษณ์ที่มีคุณสมบัติตรงตามข้อกำหนดต่อไปนี้ทั้งหมด

  • ส่งออกโดยส่วนหัวสาธารณะของไลบรารีที่ใช้ร่วมกัน
  • ปรากฏในตาราง .dynsym ของไฟล์ .so ที่สอดคล้องกับไลบรารีที่ใช้ร่วมกัน
  • มีการเชื่อมโยงแบบอ่อนหรือแบบ GLOBAL
  • ระดับการแชร์คือ "ค่าเริ่มต้น" หรือ "มีการป้องกัน"
  • ดัชนีส่วนไม่ใช่ UNDEFINED
  • ประเภทคือ FUNC หรือ OBJECT

ส่วนหัวสาธารณะของไลบรารีที่ใช้ร่วมกันจะกำหนดว่าเป็นส่วนหัวที่ใช้ได้กับไลบรารี/ไบนารีอื่นๆ ผ่านแอตทริบิวต์ export_include_dirs, export_header_lib_headers, export_static_lib_headers, export_shared_lib_headers และ export_generated_headers ในคำจำกัดความ Android.bp ของโมดูลที่สอดคล้องกับไลบรารีที่ใช้ร่วมกัน

เกี่ยวกับประเภทที่เข้าถึงได้

ประเภทที่เข้าถึงได้คือประเภท C/C++ ในตัวหรือประเภทที่ผู้ใช้กำหนดซึ่งเข้าถึงได้โดยตรงหรือโดยอ้อมผ่านสัญลักษณ์ที่ส่งออกและส่งออกผ่านส่วนหัวสาธารณะ เช่น libfoo.so มีฟังก์ชัน Foo ซึ่งเป็นสัญลักษณ์ที่ส่งออกซึ่งพบในตาราง .dynsym ไลบรารี libfoo.so ประกอบด้วยรายการต่อไปนี้

foo_exported.h foo.private.h
typedef struct foo_private foo_private_t;

typedef struct foo {
  int m1;
  int *m2;
  foo_private_t *mPfoo;
} foo_t;

typedef struct bar {
  foo_t mfoo;
} bar_t;

bool Foo(int id, bar_t *bar_ptr);
typedef struct foo_private {
  int m1;
  float mbar;
} foo_private_t;
Android.bp
cc_library {
  name : libfoo,
  vendor_available: true,
  vndk {
    enabled : true,
  }
  srcs : ["src/*.cpp"],
  export_include_dirs : [
    "exported"
  ],
}
ตาราง .dynsym
Num Value Size Type Bind Vis Ndx Name
1 0 0 FUNC GLOB DEF UND dlerror@libc
2 1ce0 20 FUNC GLOB DEF 12 Foo

เมื่อดูที่ Foo ประเภทที่เข้าถึงได้โดยตรง/โดยอ้อม ได้แก่

ประเภท คำอธิบาย
bool ประเภทการแสดงผลของ Foo
int ประเภทพารามิเตอร์ Foo แรก
bar_t * ประเภทพารามิเตอร์ Foo ที่ 2 bar_t * ส่งออก bar_t ผ่าน foo_exported.h

bar_t มีสมาชิก mfoo ประเภท foo_t ซึ่งส่งออกผ่าน foo_exported.h ซึ่งส่งผลให้มีการส่งออกประเภทอื่นๆ เพิ่มเติม ดังนี้
  • int : เป็นประเภท m1
  • int * : คือm2ประเภทหนึ่ง
  • foo_private_t * : เป็นประเภท mPfoo

อย่างไรก็ตาม foo_private_t เข้าถึงไม่ได้เนื่องจากไม่ได้ส่งออกผ่าน foo_exported.h (foo_private_t * เป็นแบบทึบ จึงอนุญาตให้ทำการเปลี่ยนแปลงกับ foo_private_t ได้)

คุณสามารถอธิบายประเภทที่เข้าถึงได้ผ่านตัวระบุคลาสพื้นฐานและพารามิเตอร์ของเทมเพลตในลักษณะที่คล้ายกันได้เช่นกัน

ปฏิบัติตามข้อกำหนด ABI

ต้องตรวจสอบการปฏิบัติตามข้อกำหนดของ ABI สำหรับไลบรารีที่มีการทำเครื่องหมาย vendor_available: true และ vndk.enabled: true ในไฟล์ Android.bp ที่เกี่ยวข้อง เช่น

cc_library {
    name: "libvndk_example",
    vendor_available: true,
    vndk: {
        enabled: true,
    }
}

สำหรับประเภทข้อมูลที่เข้าถึงได้โดยตรงหรือโดยอ้อมจากฟังก์ชันที่ส่งออก การเปลี่ยนแปลงต่อไปนี้ในไลบรารีจะจัดอยู่ในประเภท ABI-breaking

ประเภทข้อมูล คำอธิบาย
โครงสร้างและคลาส
  • เปลี่ยนขนาดของประเภทคลาสหรือประเภทโครงสร้าง
  • คลาสพื้นฐาน
    • เพิ่มหรือนำคลาสฐานออก
    • เพิ่มหรือนําคลาสพื้นฐานที่รับช่วงมาเสมือนจริงออก
    • เปลี่ยนลําดับของคลาสพื้นฐาน
  • ฟังก์ชันของสมาชิก
    • นำฟังก์ชันสมาชิกออก*
    • เพิ่มหรือนําอาร์กิวเมนต์ออกจากฟังก์ชันสมาชิก
    • เปลี่ยนประเภทอาร์กิวเมนต์หรือประเภทผลลัพธ์ของฟังก์ชันสมาชิก*
    • เปลี่ยนเลย์เอาต์ตารางเสมือน
  • สมาชิกข้อมูล
    • นำสมาชิกข้อมูลแบบคงที่ออก
    • เพิ่มหรือนําสมาชิกข้อมูลแบบไม่คงที่ออก
    • เปลี่ยนประเภทสมาชิกข้อมูล
    • เปลี่ยนออฟเซ็ตเป็นข้อความสมาชิกที่ไม่ใช่แบบคงที่**
    • เปลี่ยนตัวระบุ const, volatile และ/หรือ restricted ของสมาชิกข้อมูล***
    • ดาวน์เกรดตัวระบุการเข้าถึงของสมาชิกข้อมูล***
  • เปลี่ยนอาร์กิวเมนต์ของเทมเพลต
สหภาพแรงงาน
  • เพิ่มหรือนําสมาชิกข้อมูลออก
  • เปลี่ยนขนาดของประเภทยูเนียน
  • เปลี่ยนประเภทสมาชิกข้อมูล
การแจกแจง
  • เปลี่ยนประเภทพื้นฐาน
  • เปลี่ยนชื่อตัวนับ
  • เปลี่ยนค่าของตัวนับ
สัญลักษณ์ส่วนกลาง
  • นำสัญลักษณ์ที่ส่งออกตามส่วนหัวสาธารณะออก
  • สำหรับสัญลักษณ์สากลประเภท FUNC
    • เพิ่มหรือนำอาร์กิวเมนต์ออก
    • เปลี่ยนประเภทอาร์กิวเมนต์
    • เปลี่ยนประเภทการคืนสินค้า
    • ดาวน์เกรดตัวระบุการเข้าถึง***
  • สําหรับสัญลักษณ์ส่วนกลางประเภท OBJECT
    • เปลี่ยนประเภท C/C++ ที่เกี่ยวข้อง
    • ดาวน์เกรดตัวระบุการเข้าถึง***

* ห้ามเปลี่ยนแปลงหรือนําฟังก์ชันสมาชิกทั้งแบบสาธารณะและแบบส่วนตัวออก เนื่องจากฟังก์ชันอินไลน์แบบสาธารณะจะอ้างอิงฟังก์ชันสมาชิกแบบส่วนตัวได้ การอ้างอิงสัญลักษณ์ไปยังฟังก์ชันสมาชิกส่วนตัวจะเก็บไว้ในไบนารีของผู้เรียกได้ การเปลี่ยนหรือนําฟังก์ชันสมาชิกส่วนตัวออกจากไลบรารีที่แชร์อาจส่งผลให้ไบนารีเข้ากันไม่ได้กับเวอร์ชันเดิม

** ต้องไม่เปลี่ยนแปลงออฟเซ็ตให้กับสมาชิกของข้อมูลสาธารณะหรือส่วนตัว เนื่องจากฟังก์ชันในบรรทัดอาจอ้างถึงสมาชิกข้อมูลเหล่านี้ในส่วนเนื้อหาของฟังก์ชัน การเปลี่ยนออฟเซ็ตของสมาชิกข้อมูลอาจส่งผลให้ไบนารีเข้ากันไม่ได้กับย้อนหลัง

*** แม้ว่าจะไม่เปลี่ยนเลย์เอาต์หน่วยความจำของประเภท แต่ก็มีความแตกต่างทางความหมายที่อาจทําให้ไลบรารีไม่ทํางานตามที่คาดไว้

ใช้เครื่องมือการปฏิบัติตามข้อกำหนด ABI

เมื่อสร้างไลบรารี VNDK ระบบจะเปรียบเทียบ ABI ของไลบรารีกับข้อมูลอ้างอิง ABI ที่เกี่ยวข้องสำหรับเวอร์ชัน VNDK ที่สร้าง ข้อมูลอ้างอิง ไฟล์บันทึก ABI อยู่ในตำแหน่งต่อไปนี้

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based

เช่น ในการสร้าง libfoo สำหรับ x86 ที่ระดับ API 27 ระบบจะเปรียบเทียบ ABI ที่อนุมานของ libfoo กับข้อมูลอ้างอิงต่อไปนี้

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump

ข้อผิดพลาด ABI ที่ไม่สมบูรณ์

ในความเสียหายของ ABI บันทึกบิลด์จะแสดงคำเตือนพร้อมประเภทคำเตือนและเส้นทางไปยังรายงาน abi-diff ตัวอย่างเช่น หาก ABI ของ libbinder มีการเปลี่ยนแปลงที่เข้ากันไม่ได้ ระบบบิลด์จะแสดงข้อผิดพลาดพร้อมข้อความที่คล้ายกับข้อความต่อไปนี้

*****************************************************
error: VNDK library: libbinder.so's ABI has INCOMPATIBLE CHANGES
Please check compatibility report at:
out/soong/.intermediates/frameworks/native/libs/binder/libbinder/android_arm64_armv8-a_cortex-a73_vendor_shared/libbinder.so.abidiff
******************************************************
---- Please update abi references by running
platform/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder ----

ตรวจสอบ ABI ของไลบรารี VNDK

เมื่อสร้างไลบรารี VNDK แล้ว

  1. header-abi-dumper จะประมวลผลไฟล์ต้นทางที่คอมไพล์เพื่อสร้างไลบรารี VNDK (ไฟล์ต้นทางของไลบรารีเอง รวมถึงไฟล์ต้นทางที่รับค่าผ่าน Dependency แบบคงที่แบบเปลี่ยนผ่าน) เพื่อสร้างไฟล์ .sdump ที่สอดคล้องกับแหล่งที่มาแต่ละแห่ง
    การสร้าง sdump
    รูปที่ 1 การสร้างไฟล์ .sdump
  2. จากนั้น header-abi-linker จะประมวลผลไฟล์ .sdump (โดยใช้สคริปต์เวอร์ชันที่ระบุให้หรือไฟล์ .so ที่สอดคล้องกับไลบรารีที่ใช้ร่วมกัน) เพื่อสร้างไฟล์ .lsdump ที่บันทึกข้อมูล ABI ทั้งหมดที่เกี่ยวข้องกับไลบรารีที่ใช้ร่วมกัน
    การสร้าง lsdump
    รูปที่ 2 การสร้างไฟล์ .lsdump
  3. header-abi-diff จะเปรียบเทียบไฟล์ .lsdump กับไฟล์อ้างอิง .lsdump เพื่อสร้างรายงานความแตกต่างที่ระบุความแตกต่างใน ABI ของไลบรารี 2 รายการ
    การสร้างความแตกต่างของ ABI
    รูปที่ 3 การสร้างรายงานความแตกต่าง

header-abi-dumper

เครื่องมือ header-abi-dumper จะแยกวิเคราะห์ไฟล์ต้นฉบับ C/C++ และแสดงผล ABI ที่อนุมานจากไฟล์ต้นฉบับนั้นลงในไฟล์กลาง ระบบบิลด์จะเรียกใช้ header-abi-dumper ในไฟล์ต้นทางที่คอมไพล์แล้วทั้งหมด พร้อมกับสร้างไลบรารีที่มีไฟล์ต้นทางจาก Dependency แบบทรานซิทีฟ

อินพุต
  • ไฟล์ซอร์ส C/C++
  • ไดเรกทอรีรวมที่ส่งออก
  • แฟล็กคอมไพเลอร์
เอาต์พุต ไฟล์ที่อธิบาย ABI ของไฟล์ต้นฉบับ (เช่น foo.sdump แสดงถึง ABI ของ foo.cpp)

ปัจจุบันไฟล์ .sdump อยู่ในรูปแบบ JSON ซึ่งไม่รับประกันว่าจะเสถียรในรุ่นต่อๆ ไป ดังนั้นการจัดรูปแบบไฟล์ .sdump จึงควรถือเป็นรายละเอียดการใช้งานระบบบิลด์

เช่น libfoo.so มีไฟล์ต้นฉบับต่อไปนี้ foo.cpp

#include <stdio.h>
#include <foo_exported.h>

bool Foo(int id, bar_t *bar_ptr) {
    if (id > 0 && bar_ptr->mfoo.m1 > 0) {
        return true;
    }
    return false;
}

คุณสามารถใช้ header-abi-dumper เพื่อสร้างไฟล์ .sdump ระดับกลางที่แสดง ABI ที่ไฟล์ต้นฉบับนำเสนอได้โดยใช้

$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++

คำสั่งนี้จะบอกให้ header-abi-dumper แยกวิเคราะห์ foo.cpp ด้วย Flag คอมไพเลอร์ตามหลัง -- และแสดงข้อมูล ABI ที่ส่งออกโดยส่วนหัวสาธารณะในไดเรกทอรี exported ไฟล์ต่อไปนี้คือ foo.sdump ที่สร้างโดย header-abi-dumper

{
 "array_types" : [],
 "builtin_types" :
 [
  {
   "alignment" : 4,
   "is_integral" : true,
   "linker_set_key" : "_ZTIi",
   "name" : "int",
   "referenced_type" : "_ZTIi",
   "self_type" : "_ZTIi",
   "size" : 4
  }
 ],
 "elf_functions" : [],
 "elf_objects" : [],
 "enum_types" : [],
 "function_types" : [],
 "functions" :
 [
  {
   "function_name" : "FooBad",
   "linker_set_key" : "_Z6FooBadiP3foo",
   "parameters" :
   [
    {
     "referenced_type" : "_ZTIi"
    },
    {
     "referenced_type" : "_ZTIP3foo"
    }
   ],
   "return_type" : "_ZTI3bar",
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "global_vars" : [],
 "lvalue_reference_types" : [],
 "pointer_types" :
 [
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP11foo_private",
   "name" : "foo_private *",
   "referenced_type" : "_ZTI11foo_private",
   "self_type" : "_ZTIP11foo_private",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP3foo",
   "name" : "foo *",
   "referenced_type" : "_ZTI3foo",
   "self_type" : "_ZTIP3foo",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIPi",
   "name" : "int *",
   "referenced_type" : "_ZTIi",
   "self_type" : "_ZTIPi",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "qualified_types" : [],
 "record_types" :
 [
  {
   "alignment" : 8,
   "fields" :
   [
    {
     "field_name" : "mfoo",
     "referenced_type" : "_ZTI3foo"
    }
   ],
   "linker_set_key" : "_ZTI3bar",
   "name" : "bar",
   "referenced_type" : "_ZTI3bar",
   "self_type" : "_ZTI3bar",
   "size" : 24,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "fields" :
   [
    {
     "field_name" : "m1",
     "referenced_type" : "_ZTIi"
    },
    {
     "field_name" : "m2",
     "field_offset" : 64,
     "referenced_type" : "_ZTIPi"
    },
    {
     "field_name" : "mPfoo",
     "field_offset" : 128,
     "referenced_type" : "_ZTIP11foo_private"
    }
   ],
   "linker_set_key" : "_ZTI3foo",
   "name" : "foo",
   "referenced_type" : "_ZTI3foo",
   "self_type" : "_ZTI3foo",
   "size" : 24,
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "rvalue_reference_types" : []
}

foo.sdump มีข้อมูล ABI ที่ส่งออกโดยไฟล์ต้นฉบับ foo.cpp และส่วนหัวสาธารณะ เช่น

  • record_types หมายถึงโครงสร้าง ยูเนียน หรือคลาสที่กําหนดไว้ในส่วนหัวสาธารณะ ระเบียนแต่ละประเภทจะมีข้อมูลเกี่ยวกับช่อง ขนาด ตัวระบุการเข้าถึง ไฟล์ส่วนหัวที่กำหนดไว้ และแอตทริบิวต์อื่นๆ
  • pointer_types. อ้างอิงถึงประเภทพอยน์เตอร์โดยตรง/โดยอ้อมที่บันทึกโดยระเบียน/ฟังก์ชันที่ส่งออกในส่วนหัวสาธารณะ พร้อมกับประเภทที่พอยน์เตอร์ชี้ไป (ผ่านช่อง referenced_type ใน type_info) ระบบจะบันทึกข้อมูลที่คล้ายกันในไฟล์ .sdump สำหรับประเภทที่ผ่านการรับรอง ประเภท C/C++ ในตัว ประเภทอาร์เรย์ และประเภทข้อมูลอ้างอิง lvalue และ rvalue ข้อมูลดังกล่าวช่วยให้สามารถเปรียบเทียบแบบย้อนกลับได้
  • functions แสดงฟังก์ชันที่ส่งออกโดยส่วนหัวสาธารณะ นอกจากนี้ยังมีข้อมูลเกี่ยวกับชื่อที่ลบการซ้อนกันของฟังก์ชัน ประเภทผลลัพธ์ ประเภทของพารามิเตอร์ ตัวระบุการเข้าถึง และแอตทริบิวต์อื่นๆ

header-abi-linker

เครื่องมือ header-abi-linker จะใช้ไฟล์กลางที่ header-abi-dumper สร้างขึ้นเป็นอินพุต จากนั้นจะลิงก์ไฟล์เหล่านั้น

อินพุต
  • ไฟล์กลางที่ header-abi-dumper สร้างขึ้น
  • ไฟล์สคริปต์เวอร์ชัน/ไฟล์แผนที่ (ไม่บังคับ)
  • ไฟล์ .so ของไลบรารีที่ใช้ร่วมกัน
  • ไดเรกทอรีรวมที่ส่งออก
เอาต์พุต ไฟล์ที่อธิบาย ABI ของไลบรารีที่แชร์ (เช่น libfoo.so.lsdump แสดงถึง ABI ของ libfoo)

เครื่องมือจะผสานกราฟประเภทในไฟล์กลางทั้งหมดที่ได้รับ โดยพิจารณาความแตกต่างของหน่วยการแปลแต่ละหน่วยที่มีคำจำกัดความเดียว (ประเภทที่ผู้ใช้กำหนดในหน่วยการแปลที่แตกต่างกันซึ่งมีชื่อเต็มเดียวกัน อาจแตกต่างกันตามความหมาย) จากนั้นเครื่องมือจะแยกวิเคราะห์สคริปต์เวอร์ชันหรือตาราง .dynsym ของไลบรารีที่ใช้ร่วมกัน (ไฟล์ .so) เพื่อสร้างรายการสัญลักษณ์ที่ส่งออก

เช่น libfoo ประกอบด้วย foo.cpp และ bar.cpp header-abi-linker สามารถเรียกใช้เพื่อสร้างไฟล์ข้อมูล ABI ที่ลิงก์สมบูรณ์ของ libfoo ดังนี้

header-abi-linker -I exported foo.sdump bar.sdump \
                  -o libfoo.so.lsdump \
                  -so libfoo.so \
                  -arch arm64 -api current

ตัวอย่างเอาต์พุตคำสั่งใน libfoo.so.lsdump

{
 "array_types" : [],
 "builtin_types" :
 [
  {
   "alignment" : 1,
   "is_integral" : true,
   "is_unsigned" : true,
   "linker_set_key" : "_ZTIb",
   "name" : "bool",
   "referenced_type" : "_ZTIb",
   "self_type" : "_ZTIb",
   "size" : 1
  },
  {
   "alignment" : 4,
   "is_integral" : true,
   "linker_set_key" : "_ZTIi",
   "name" : "int",
   "referenced_type" : "_ZTIi",
   "self_type" : "_ZTIi",
   "size" : 4
  }
 ],
 "elf_functions" :
 [
  {
   "name" : "_Z3FooiP3bar"
  },
  {
   "name" : "_Z6FooBadiP3foo"
  }
 ],
 "elf_objects" : [],
 "enum_types" : [],
 "function_types" : [],
 "functions" :
 [
  {
   "function_name" : "Foo",
   "linker_set_key" : "_Z3FooiP3bar",
   "parameters" :
   [
    {
     "referenced_type" : "_ZTIi"
    },
    {
     "referenced_type" : "_ZTIP3bar"
    }
   ],
   "return_type" : "_ZTIb",
   "source_file" : "exported/foo_exported.h"
  },
  {
   "function_name" : "FooBad",
   "linker_set_key" : "_Z6FooBadiP3foo",
   "parameters" :
   [
    {
     "referenced_type" : "_ZTIi"
    },
    {
     "referenced_type" : "_ZTIP3foo"
    }
   ],
   "return_type" : "_ZTI3bar",
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "global_vars" : [],
 "lvalue_reference_types" : [],
 "pointer_types" :
 [
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP11foo_private",
   "name" : "foo_private *",
   "referenced_type" : "_ZTI11foo_private",
   "self_type" : "_ZTIP11foo_private",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP3bar",
   "name" : "bar *",
   "referenced_type" : "_ZTI3bar",
   "self_type" : "_ZTIP3bar",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP3foo",
   "name" : "foo *",
   "referenced_type" : "_ZTI3foo",
   "self_type" : "_ZTIP3foo",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIPi",
   "name" : "int *",
   "referenced_type" : "_ZTIi",
   "self_type" : "_ZTIPi",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "qualified_types" : [],
 "record_types" :
 [
  {
   "alignment" : 8,
   "fields" :
   [
    {
     "field_name" : "mfoo",
     "referenced_type" : "_ZTI3foo"
    }
   ],
   "linker_set_key" : "_ZTI3bar",
   "name" : "bar",
   "referenced_type" : "_ZTI3bar",
   "self_type" : "_ZTI3bar",
   "size" : 24,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "fields" :
   [
    {
     "field_name" : "m1",
     "referenced_type" : "_ZTIi"
    },
    {
     "field_name" : "m2",
     "field_offset" : 64,
     "referenced_type" : "_ZTIPi"
    },
    {
     "field_name" : "mPfoo",
     "field_offset" : 128,
     "referenced_type" : "_ZTIP11foo_private"
    }
   ],
   "linker_set_key" : "_ZTI3foo",
   "name" : "foo",
   "referenced_type" : "_ZTI3foo",
   "self_type" : "_ZTI3foo",
   "size" : 24,
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "rvalue_reference_types" : []
}

เครื่องมือ header-abi-linker

  • ลิงก์ไฟล์ .sdump ที่ให้ไว้ (foo.sdump และ bar.sdump) ซึ่งกรองข้อมูล ABI ที่ไม่อยู่ในส่วนหัวที่อยู่ในไดเรกทอรีออก: exported
  • แยกวิเคราะห์ libfoo.so และรวบรวมข้อมูลเกี่ยวกับสัญลักษณ์ที่ส่งออกโดยไลบรารีผ่านตาราง .dynsym
  • เพิ่ม _Z3FooiP3bar และ _Z6FooBadiP3foo

libfoo.so.lsdump เป็นดัมพ์ ABI ที่สร้างขึ้นขั้นสุดท้ายของ libfoo.so

header-abi-diff

เครื่องมือ header-abi-diff จะเปรียบเทียบไฟล์ .lsdump 2 ไฟล์ที่แสดง ABI ของไลบรารี 2 รายการ และสร้างรายงานความแตกต่างที่ระบุความแตกต่างระหว่าง ABI 2 รายการ

อินพุต
  • .lsdump ไฟล์ที่แสดง ABI ของไลบรารีที่ใช้ร่วมกันเก่า
  • ไฟล์ .lsdump ที่แสดง ABI ของไลบรารีที่ใช้ร่วมกันใหม่
เอาต์พุต รายงาน Diff ที่ระบุความแตกต่างของ ABI ที่นำเสนอโดยไลบรารีที่ใช้ร่วมกัน 2 รายการที่เปรียบเทียบ

ไฟล์ความแตกต่างของ ABI อยู่ใน รูปแบบข้อความ Proโตบัฟ รูปแบบอาจมีการเปลี่ยนแปลงในรุ่นต่อๆ ไป

เช่น คุณมี libfoo 2 เวอร์ชัน ได้แก่ libfoo_old.so และ libfoo_new.so ใน libfoo_new.so ในส่วน bar_t คุณได้เปลี่ยนประเภทของ mfoo จาก foo_t เป็น foo_t * เนื่องจาก bar_t เป็นประเภทที่เข้าถึงได้ header-abi-diff จึงควรแจ้งว่าการเปลี่ยนแปลงนี้เป็นการเปลี่ยนแปลงที่ขัดต่อ ABI

วิธีเรียกใช้ header-abi-diff

header-abi-diff -old libfoo_old.so.lsdump \
                -new libfoo_new.so.lsdump \
                -arch arm64 \
                -o libfoo.so.abidiff \
                -lib libfoo

ตัวอย่างเอาต์พุตจากคำสั่งใน libfoo.so.abidiff

lib_name: "libfoo"
arch: "arm64"
record_type_diffs {
  name: "bar"
  type_stack: "Foo-> bar *->bar "
  type_info_diff {
    old_type_info {
      size: 24
      alignment: 8
    }
    new_type_info {
      size: 8
      alignment: 8
    }
  }
  fields_diff {
    old_field {
      referenced_type: "foo"
      field_offset: 0
      field_name: "mfoo"
      access: public_access
    }
    new_field {
      referenced_type: "foo *"
      field_offset: 0
      field_name: "mfoo"
      access: public_access
    }
  }
}

libfoo.so.abidiff มีรายงานการเปลี่ยนแปลงที่ส่งผลกับส่วนอื่นในระบบ ABI ทั้งหมดใน libfoo ข้อความ record_type_diffs บ่งบอกว่าระเบียนมีการเปลี่ยนแปลงและแสดงรายการการเปลี่ยนแปลงที่เข้ากันไม่ได้ ซึ่งได้แก่

  • ขนาดของระเบียนเปลี่ยนจาก 24 ไบต์เป็น 8 ไบต์
  • ประเภทช่องของ mfoo เปลี่ยนจาก foo เป็น foo * (ลบ typedef ทั้งหมดออก)

ช่อง type_stack ระบุวิธีที่ header-abi-diff เข้าถึงประเภทที่เปลี่ยนแปลง (bar) ช่องนี้อาจตีความได้ว่า Foo เป็นฟังก์ชันที่ส่งออกที่ใช้ bar * เป็นพารามิเตอร์ ซึ่งชี้ไปยัง bar ซึ่งส่งออกและเปลี่ยนแปลง

บังคับใช้ ABI และ API

หากต้องการบังคับใช้ ABI และ API ของไลบรารีที่ใช้ร่วมกันของ VNDK คุณต้องตรวจสอบการอ้างอิง ABI ใน ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/ หากต้องการสร้างการอ้างอิงเหล่านี้ ให้เรียกใช้คําสั่งต่อไปนี้

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py

หลังจากสร้างการอ้างอิงแล้ว การเปลี่ยนแปลงใดๆ ในซอร์สโค้ดที่ส่งผลให้เกิดการเปลี่ยนแปลง ABI/API ที่เข้ากันไม่ได้ในไลบรารี VNDK จะทำให้เกิดข้อผิดพลาดในการบิลด์

หากต้องการอัปเดตการอ้างอิง ABI สำหรับไลบรารีที่เฉพาะเจาะจง ให้เรียกใช้คำสั่งต่อไปนี้

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>

เช่น หากต้องการอัปเดตข้อมูลอ้างอิง ABI ของ libbinder ให้เรียกใช้คำสั่งต่อไปนี้

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder