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

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

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

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

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

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

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

  • ส่งออกโดยส่วนหัวสาธารณะของไลบรารีที่ใช้ร่วมกัน
  • ปรากฏในตาราง .dynsym ของไฟล์ .so ที่สอดคล้องกับไลบรารีที่ใช้ร่วมกัน
  • มีการเชื่อมโยง WEAK หรือการเชื่อมโยงทั่วโลก
  • ระดับการแชร์เป็น "ค่าเริ่มต้น" หรือ "ป้องกัน"
  • ดัชนีส่วนไม่อยู่ในเกณฑ์
  • ประเภทอาจเป็น 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

ประเภทข้อมูล คำอธิบาย
โครงสร้างและชั้นเรียน
  • เปลี่ยนขนาดของประเภทคลาสหรือประเภทโครงสร้าง
  • คลาสเบส
    • เพิ่มหรือนำคลาสฐานออก
    • เพิ่มหรือนำคลาสพื้นฐานที่รับค่ามาทางเสมือนออก
    • เปลี่ยนลำดับของคลาสพื้นฐาน
  • ฟังก์ชันสมาชิก
    • นำฟังก์ชันสมาชิกออก*
    • เพิ่มหรือนำอาร์กิวเมนต์ออกจากฟังก์ชันสมาชิก
    • เปลี่ยนประเภทอาร์กิวเมนต์หรือประเภทการแสดงผลของสมาชิก ฟังก์ชัน*
    • เปลี่ยนเลย์เอาต์ตารางเสมือน
  • สมาชิกข้อมูล
    • นำสมาชิกข้อมูลคงที่ออก
    • เพิ่มหรือนำสมาชิกข้อมูลที่ไม่ใช่แบบคงที่ออก
    • เปลี่ยนประเภทสมาชิกข้อมูล
    • เปลี่ยนออฟเซ็ตเป็นข้อความสมาชิกที่ไม่ใช่แบบคงที่**
    • เปลี่ยน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 ของ 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 การสร้างรายงานความแตกต่าง

ส่วนหัว 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 โดยมีแฟล็กคอมไพเลอร์ตามหลัง -- และ แสดงข้อมูล 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 แสดงฟังก์ชันที่ส่งออกโดยส่วนหัวสาธารณะ และยังมีข้อมูลเกี่ยวกับชื่อที่เปลี่ยนแปลงของฟังก์ชัน ประเภทการแสดงผล ประเภทของพารามิเตอร์ ตัวระบุการเข้าถึง และแอตทริบิวต์อื่นๆ

ส่วนหัว-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 ไปยัง สร้างไฟล์ Dump ของ 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

ส่วนหัว-abi-diff

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

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

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

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

วิธีเรียกใช้ 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