يُعدّ ثبات واجهة التطبيق الثنائية (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.
- يجب أن يكون مستوى العرض DEFAULT أو PROTECTED.
- يجب ألا يكون فهرس القسم 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،
  ما يؤدي إلى تصدير المزيد من الأنواع:
 ومع ذلك، لا يمكن الوصول إلى 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):
| نوع البيانات | الوصف | 
|---|---|
| البُنى والفئات | 
 | 
| الاتحادات | 
 | 
| التعدادات | 
 | 
| الرموز العالمية | 
 | 
* يجب عدم تغيير أو إزالة دوال الأعضاء العلنية والخاصة لأنّ الدوال المضمّنة العلنية يمكن أن تشير إلى دوال الأعضاء الخاصة. يمكن الاحتفاظ بمراجع الرموز لدوال الأعضاء الخاصة في الملفات الثنائية الخاصة بالمتصل. قد يؤدي تغيير أو إزالة دوال الأعضاء الخاصة من المكتبات المشتركة إلى إنشاء ملفات ثنائية غير متوافقة مع الإصدارات السابقة.
** يجب عدم تغيير الإزاحات إلى عناصر البيانات العامة أو الخاصة، لأنّ الدوال المضمّنة يمكن أن تشير إلى عناصر البيانات هذه في نص الدالة. يمكن أن يؤدي تغيير إزاحات أعضاء البيانات إلى إنشاء ملفات ثنائية غير متوافقة مع الإصدارات السابقة.
*** على الرغم من أنّ هذه التغييرات لا تؤثر في تخطيط الذاكرة الخاص بالنوع، إلا أنّ هناك اختلافات دلالية قد تؤدي إلى عدم عمل المكتبات على النحو المتوقّع.
استخدام أدوات التوافق مع واجهة التطبيق الثنائية (ABI)
عند إنشاء مكتبة VNDK، تتم مقارنة واجهة ABI الخاصة بالمكتبة مع مرجع واجهة ABI المقابل لإصدار VNDK الذي يتم إنشاؤه. المراجع يمكن العثور على عمليات تفريغ ABI في:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
  على سبيل المثال، عند إنشاء libfoo لبنية x86 على مستوى واجهة برمجة التطبيقات 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:
- تعالج header-abi-dumperملفات المصدر التي تم تجميعها لإنشاء مكتبة VNDK (ملفات المصدر الخاصة بالمكتبة بالإضافة إلى ملفات المصدر الموروثة من خلال التبعيات المتعدية الثابتة)، وذلك لإنتاج ملفات.sdumpالتي تتوافق مع كل مصدر.
   الشكل 1. إنشاء ملفات .sdump
- تعالج أداة header-abi-linkerبعد ذلك ملفات.sdump(باستخدام نص برمجي للإصدار يتم توفيره لها أو ملف.soالمقابل للمكتبة المشترَكة) لإنتاج ملف.lsdumpيسجّل جميع معلومات واجهة التطبيق الثنائية (ABI) المقابلة للمكتبة المشترَكة.
   الشكل 2. جارٍ إنشاء الملف .lsdump
- تقارن أداة header-abi-diffملف.lsdumpبملف.lsdumpمرجعي لإنشاء تقرير diff يوضّح الاختلافات في واجهات ABI للمكتبتَين.
   الشكل 3. إنشاء تقرير الاختلاف 
header-abi-dumper
  تحلّل أداة header-abi-dumper ملف مصدر C/C++ وتُفرغ
  واجهة التطبيق الثنائية (ABI) المستنتَجة من ملف المصدر هذا في ملف وسيط. ينفّذ نظام الإنشاء header-abi-dumper على جميع ملفات المصدر التي تم تجميعها، كما ينشئ مكتبة تتضمّن ملفات المصدر من التبعيات المتعدية.
| مداخل | 
 | 
|---|---|
| الإخراج | ملف يصف واجهة التطبيق الثنائية (ABI) للملف المصدر (على سبيل المثال، 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++ المضمّنة وأنواع المصفوفات وأنواع مراجع القيم اليسرى واليمنى. تتيح هذه المعلومات إجراء مقارنة تفاضلية متكررة.
- functions: تمثّل الدوال التي يتم تصديرها بواسطة العناوين العامة. وتتضمّن أيضًا معلومات حول الاسم المشوّه للدالة ونوع القيمة المرجَعة وأنواع المَعلمات ومحدّد الوصول وسمات أخرى.
header-abi-linker
  تستخدم الأداة header-abi-linker الملفات الوسيطة التي تنتجها header-abi-dumper كمدخلات ثم تربط هذه الملفات:
| مداخل | 
 | 
|---|---|
| الإخراج | ملف يصف واجهة التطبيق الثنائية (ABI) لمكتبة مشترَكة (على سبيل المثال، libfoo.so.lsdump تمثّل واجهة التطبيق الثنائية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 يمثّلان واجهة التطبيق الثنائية (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 *(تتم إزالة جميع تعريفات الأنواع).
  يشير الحقل type_stack إلى كيفية وصول header-abi-diff إلى النوع الذي تم تغييره (bar). ويمكن تفسير هذا الحقل على أنّه Foo عبارة عن دالة تم تصديرها وتتضمّن bar * كمعلَمة تشير إلى bar الذي تم تصديره وتغييره.
فرض استخدام واجهة ABI وواجهة برمجة التطبيقات
  لفرض واجهة التطبيق الثنائية (ABI) وواجهة برمجة التطبيقات (API) للمكتبات المشتركة في VNDK، يجب إيداع مراجع واجهة التطبيق الثنائية في ${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
