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

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

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

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

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

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

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

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

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

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

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

ประเภทข้อมูล คำอธิบาย
โครงสร้างและชั้นเรียน
  • เปลี่ยนขนาดของประเภทคลาสหรือประเภทโครงสร้าง
  • คลาสพื้นฐาน
    • เพิ่มหรือนำคลาสพื้นฐานออก
    • เพิ่มหรือนำคลาสฐานที่รับค่าแบบเสมือนออก
    • เปลี่ยนลำดับของคลาสฐาน
  • ฟังก์ชันสมาชิก
    • ฟังก์ชันการนำสมาชิกออก*
    • เพิ่มหรือนำอาร์กิวเมนต์ออกจากฟังก์ชันสมาชิก
    • เปลี่ยนประเภทอาร์กิวเมนต์หรือประเภทการคืนค่าของฟังก์ชันสมาชิก*
    • เปลี่ยนเลย์เอาต์ตารางเสมือน
  • สมาชิกข้อมูล
    • นำสมาชิกข้อมูลแบบคงที่ออก
    • เพิ่มหรือนำสมาชิกข้อมูลที่ไม่ใช่แบบคงที่ออก
    • เปลี่ยนประเภทของสมาชิกข้อมูล
    • เปลี่ยนออฟเซ็ตเป็นสมาชิกข้อมูลที่ไม่คงที่**
    • เปลี่ยนตัวระบุ const, volatile และ/หรือ restricted ของสมาชิกข้อมูล***
    • ลดระดับตัวระบุการเข้าถึงของสมาชิกข้อมูล***
  • เปลี่ยนอาร์กิวเมนต์ของเทมเพลต
สหภาพ
  • เพิ่มหรือนำสมาชิกข้อมูลออก
  • เปลี่ยนขนาดของประเภท Union
  • เปลี่ยนประเภทของสมาชิกข้อมูล
การแจงนับ
  • เปลี่ยนประเภทพื้นฐาน
  • เปลี่ยนชื่อผู้แจงนับ
  • เปลี่ยนค่าของตัวแจงนับ
สัญลักษณ์ทั่วโลก
  • นำสัญลักษณ์ที่ส่งออกจากส่วนหัวสาธารณะออก
  • สำหรับสัญลักษณ์ส่วนกลางประเภท 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 เพื่อสร้างรายงาน Diff ที่ระบุความแตกต่างใน ABI ของไลบรารีทั้ง 2
    การสร้างความแตกต่างของ ABI
    รูปที่ 3 การสร้างรายงานความแตกต่าง

header-abi-dumper

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

อินพุต
  • ไฟล์ต้นฉบับ C/C++
  • ส่งออกไดเรกทอรีรวม
  • แฟล็กคอมไพเลอร์
เอาต์พุต ไฟล์ที่อธิบาย ABI ของไฟล์ต้นฉบับ (เช่น ABI ของ foo.sdump represents 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 แสดงฟังก์ชันที่ส่งออกโดยส่วนหัวสาธารณะ นอกจากนี้ ยังมีข้อมูลเกี่ยวกับชื่อที่ดัดแปลงของฟังก์ชัน ประเภทการคืนค่า ประเภทของพารามิเตอร์ ตัวระบุการเข้าถึง และแอตทริบิวต์อื่นๆ

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 รายการ และสร้างรายงาน Diff ที่ระบุ ความแตกต่างระหว่าง ABI ทั้ง 2 รายการ

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

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

เช่น คุณมี 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