پایداری ABI

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

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

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

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

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

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

  • صادر شده توسط سرصفحه های عمومی یک کتابخانه مشترک.
  • در جدول .dynsym فایل .so مربوط به کتابخانه مشترک ظاهر می شود.
  • دارای اتصال ضعیف یا جهانی است.
  • قابلیت مشاهده پیش‌فرض یا محافظت شده است.
  • شاخص بخش تعریف نشده نیست.
  • نوع یا 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 اطمینان حاصل کنید

برای کتابخانه‌هایی که vendor_available: true و vndk.enabled: true در فایل‌های Android.bp مربوطه دارند، باید از انطباق ABI اطمینان حاصل شود. به عنوان مثال:

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

برای انواع داده‌هایی که به‌طور مستقیم یا غیرمستقیم توسط یک تابع صادر شده قابل دسترسی هستند، تغییرات زیر در یک کتابخانه به‌عنوان ABI-breaking طبقه‌بندی می‌شوند:

نوع داده توضیحات
سازه ها و کلاس ها
  • اندازه نوع کلاس یا نوع ساختار را تغییر دهید.
  • کلاس های پایه
    • اضافه کردن یا حذف کلاس های پایه
    • کلاس های پایه تقریباً به ارث رسیده را اضافه یا حذف کنید.
    • ترتیب کلاس های پایه را تغییر دهید.
  • توابع عضو
    • حذف توابع عضو*.
    • اضافه کردن یا حذف آرگومان ها از توابع عضو.
    • انواع آرگومان یا نوع بازگشتی توابع عضو* را تغییر دهید.
    • طرح جدول مجازی را تغییر دهید.
  • اعضای داده
    • اعضای داده استاتیک را حذف کنید.
    • اعضای داده غیر ایستا را اضافه یا حذف کنید.
    • نوع اعضای داده را تغییر دهید.
    • افست ها را به اعضای داده غیر ایستا تغییر دهید**.
    • متغیرهای 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. ایجاد گزارش تفاوت

header-abi-dumper

ابزار 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-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 dump پیوندی 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 را که نمایانگر ABI دو کتابخانه هستند، مقایسه می‌کند و یک گزارش تفاوت تولید می‌کند که تفاوت‌های بین دو ABI را بیان می‌کند.

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

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

به عنوان مثال، شما دو نسخه از 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 * (همه تایپ‌دف‌ها حذف می‌شوند).

فیلد 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