پایداری ABI

پایداری رابط دودویی برنامه (ABI) پیش‌نیاز به‌روزرسانی‌های صرفاً چارچوب است، زیرا ماژول‌های فروشنده ممکن است به کتابخانه‌های اشتراکی کیت توسعه بومی فروشنده (VNDK) که در پارتیشن سیستم قرار دارند، وابسته باشند. در یک نسخه اندروید، کتابخانه‌های اشتراکی VNDK که به تازگی ساخته شده‌اند باید با کتابخانه‌های اشتراکی VNDK که قبلاً منتشر شده‌اند، از نظر ABI سازگار باشند تا ماژول‌های فروشنده بتوانند بدون کامپایل مجدد و بدون خطاهای زمان اجرا با آن کتابخانه‌ها کار کنند. بین نسخه‌های اندروید، کتابخانه‌های VNDK می‌توانند تغییر کنند و هیچ تضمینی برای ABI وجود ندارد.

برای اطمینان از سازگاری ABI، اندروید ۹ شامل یک بررسی‌کننده‌ی ABI هدر است که در بخش‌های بعدی توضیح داده شده است.

درباره انطباق با VNDK و ABI

VNDK مجموعه‌ای محدود از کتابخانه‌ها است که ماژول‌های فروشنده ممکن است به آنها پیوند داشته باشند و به‌روزرسانی‌های فقط چارچوب را فعال می‌کنند. انطباق با ABI به توانایی یک نسخه جدیدتر از یک کتابخانه مشترک برای کار مطابق انتظار با ماژولی که به صورت پویا به آن پیوند داده شده است (یعنی مانند نسخه قدیمی‌تر کتابخانه کار می‌کند) اشاره دارد.

درباره نمادهای صادر شده

یک نماد صادر شده (که به عنوان نماد سراسری نیز شناخته می‌شود) به نمادی اشاره دارد که تمام موارد زیر را برآورده می‌کند:

  • توسط سرآیندهای عمومی یک کتابخانه مشترک صادر می‌شود.
  • در جدول .dynsym ‎ مربوط به کتابخانه مشترک، در فایل .so ‎ نمایش داده می‌شود.
  • دارای الزام WEAK یا GLOBAL است.
  • قابلیت مشاهده پیش‌فرض یا محافظت‌شده است.
  • فهرست بخش تعریف نشده است.
  • نوع یا 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;
اندروید.بی‌پی
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 باید برای کتابخانه‌هایی که در فایل‌های Android.bp مربوطه با vendor_available: true و vndk.enabled: true علامت‌گذاری شده‌اند، تضمین شود. برای مثال:

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 ----

ساخت کتابخانه VNDK برای بررسی ABI

وقتی یک کتابخانه VNDK ساخته می‌شود:

  1. header-abi-dumper فایل‌های منبع کامپایل شده برای ساخت کتابخانه VNDK (فایل‌های منبع خود کتابخانه و همچنین فایل‌های منبع به ارث رسیده از طریق وابستگی‌های انتقالی استاتیک) را پردازش می‌کند تا فایل‌های .sdump مربوط به هر منبع را تولید کند.
    sdump creation
    شکل ۱. ایجاد فایل‌های .sdump
  2. سپس header-abi-linker فایل‌های .sdump را (با استفاده از اسکریپت نسخه ارائه شده به آن یا فایل .so مربوط به کتابخانه مشترک) پردازش می‌کند تا یک فایل .lsdump تولید کند که تمام اطلاعات ABI مربوط به کتابخانه مشترک را ثبت می‌کند.
    lsdump creation
    شکل ۲. ایجاد فایل .lsdump
  3. header-abi-diff فایل .lsdump را با یک فایل .lsdump مرجع مقایسه می‌کند تا یک گزارش diff تولید کند که تفاوت‌های ABI های دو کتابخانه را مشخص می‌کند.
    abi diff creation
    شکل ۳. ایجاد گزارش تفاوت

هدر-ابی-دامپر

ابزار 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 . نشان‌دهنده توابعی هستند که توسط هدرهای عمومی صادر می‌شوند. آن‌ها همچنین اطلاعاتی در مورد نام دستکاری‌شده تابع، نوع بازگشتی، انواع پارامترها، مشخص‌کننده دسترسی و سایر ویژگی‌ها دارند.

هدر-ابی-لینکر

ابزار 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 می‌تواند برای ایجاد یک نسخه پشتیبان کامل از 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 دو فایل .lsdump که نشان‌دهنده ABI دو کتابخانه هستند را مقایسه می‌کند و یک گزارش diff تولید می‌کند که تفاوت‌های بین دو 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 breaking توسط 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 breaking در libfoo است. پیام record_type_diffs نشان می‌دهد که یک رکورد تغییر کرده است و تغییرات ناسازگار را فهرست می‌کند، که شامل موارد زیر است:

  • اندازه رکورد از 24 بایت به 8 بایت تغییر می‌کند.
  • نوع فیلد mfoo از foo به foo * تغییر می‌کند (تمام typedefها حذف می‌شوند).

فیلد type_stack نشان می‌دهد که header-abi-diff چگونه به نوعی که تغییر کرده ( bar ) رسیده است. این فیلد را می‌توان به این صورت تفسیر کرد که Foo یک تابع export شده است که bar * را به عنوان پارامتر می‌گیرد، که به bar اشاره می‌کند، که export شده و تغییر کرده است.

اعمال 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