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

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

เพื่อช่วยรับประกันความเข้ากันได้ของ ABI นั้น Android 9 มีตัวตรวจสอบ 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 ตัวที่สอง โดยวิธี 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-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 (ไฟล์ต้นฉบับของไลบรารีเองตลอดจนไฟล์ต้นฉบับที่สืบทอดผ่านการพึ่งพาสกรรมกริยาแบบคงที่) เพื่อสร้างไฟล์ .sdump ที่สอดคล้องกับแต่ละแหล่ง
    sdump creation
    รูปที่ 1 การสร้างไฟล์ .sdump
  2. จากนั้น header-abi-linker จะประมวลผลไฟล์ .sdump (โดยใช้สคริปต์เวอร์ชันที่จัดเตรียมไว้ให้หรือไฟล์ .so ที่สอดคล้องกับไลบรารีที่แบ่งใช้) เพื่อสร้างไฟล์ .lsdump ที่บันทึกข้อมูล ABI ทั้งหมดที่สอดคล้องกับไลบรารีที่แบ่งใช้
    lsdump creation
    รูปที่ 2 การสร้างไฟล์ .lsdump
  3. header-abi-diff จะเปรียบเทียบไฟล์ .lsdump กับไฟล์อ้างอิง .lsdump เพื่อสร้างรายงานความแตกต่างที่สรุปความแตกต่างใน ABI ของทั้งสองไลบรารี
    abi diff creation
    รูปที่ 3 การสร้างรายงานความแตกต่าง

ส่วนหัว-abi-รถเท

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

อินพุต
  • ไฟล์ต้นฉบับ AC/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 สามารถเรียกใช้เพื่อสร้างดัมพ์ 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 สองไฟล์ที่แสดงถึง ABI ของสองไลบรารี และสร้างรายงานที่แตกต่างที่ระบุความแตกต่างระหว่าง ABI ทั้งสอง

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

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

ตัวอย่างเช่น คุณมี 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 * (typedefs ทั้งหมดจะถูกถอดออก)

ฟิลด์ 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>

ตัวอย่างเช่น หากต้องการอัพเดตการอ้างอิง libbinder ABI ให้รัน:

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