يُعدّ ثبات واجهة التطبيق الثنائية (ABI) شرطًا أساسيًا للتحديثات التي تتضمّن إطار عمل فقط، لأنّ وحدات المورّد قد تعتمد على المكتبات المشترَكة في "حزمة تطوير البرامج الأصلية الخاصة بالمورّد" (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 وواجهة برمجة التطبيقات للمكتبات المشتركة في 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