يُعدّ ثبات واجهة التطبيق الثنائية (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