پایداری ABI

پایداری «میانای دودویی کاربردی» (ABI) پیش‌نیاز به‌روزرسانی‌های فقط چارچوب است، زیرا ممکن است ماژول‌های فروشنده به کتابخانه‌های مشترک «کیت توسعه بومی فروشنده» (VNDK) که در بخش سیستم قرار دارند وابسته باشند. در نسخه Android، کتابخانه‌های مشترک VNDK جدیداً ساخته‌شده باید با کتابخانه‌های مشترک VNDK قبلاً منتشرشده سازگار با ABI باشند تا ماژول‌های فروشنده بتوانند بدون بازهمگردانی و بدون خطاهای زمان اجرا با آن کتابخانه‌ها کار کنند. بین نسخه‌های 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 در سطح میانای برنامه‌سازی کاربردی ۲۷، میانای دودویی کاربردی استنباط‌شده 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
    شکل ۱. درحال ایجاد .sdump فایل
  2. header-abi-linker سپس فایل‌های .sdump را پردازش می‌کند (بااستفاده از اسکریپت نسخه‌ای که به آن ارائه شده است یا فایل .so مربوط به کتابخانه هم‌رسانی‌شده) تا فایل .lsdump را تولید کند که همه اطلاعات ABI مربوط به کتابخانه هم‌رسانی‌شده را ثبت می‌کند.
    ایجاد lsdump
    شکل ۲. درحال ایجاد فایل .lsdump
  3. header-abi-diff فایل .lsdump را با فایل مرجع .lsdump مقایسه می‌کند تا گزارش تفاوت تولید کند که تفاوت‌های موجود در ABI دو کتابخانه را مشخص می‌کند.
    ساخت تفاوت abi
    شکل ۳. درحال ایجاد گزارش تفاوت

header-abi-dumper

ابزار header-abi-dumper فایل منبع C/C++ را تجزیه می‌کند و «میانای کاربردی باینری» استنباط‌شده از آن فایل منبع را در یک فایل واسطه تخلیه می‌کند. سیستم ساخت header-abi-dumper را روی همه فایل‌های منبع کامپایل‌شده اجرا می‌کند و درعین‌حال کتابخانه‌ای می‌سازد که شامل فایل‌های منبع از وابستگی‌های گذاراست.

ورودی‌ها
  • فایل منبع C/C++‎
  • فهرست‌های راهنمای شامل صادرشده
  • پرچم‌های گردآورنده
خروجی فایلی که «واسط کاربردی نرم‌افزار» فایل منبع را توصیف می‌کند (برای مثال، foo.sdump واسط کاربردی نرم‌افزار 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 است.

تفاوت سرایند ABI

ابزار header-abi-diff دو فایل .lsdump را که نشان‌دهنده ABI دو کتابخانه هستند مقایسه می‌کند و گزارش تفاوتی تولید می‌کند که تفاوت‌های بین دو ABI را بیان می‌کند.

ورودی‌ها
  • .lsdump فایل نشان‌دهنده ABI کتابخانه مشترک قدیمی.
  • فایل .lsdump نشان‌دهنده ABI کتابخانه مشترک جدید.
خروجی گزارش diff که تفاوت‌های ABI ارائه‌شده توسط دو کتابخانه مشترک مقایسه‌شده را بیان می‌کند.

فایل تفاوت ABI در قالب نوشتاری protobuf است. قالب ممکن است در نسخه‌های پخش آینده تغییر کند.

برای مثال، دو نسخه از libfoo دارید: 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 breaking در 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>

برای مثال، برای به‌روزرسانی ارجاع‌های libbinder ABI، این دستور را اجرا کنید:

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