ثبات واجهة التطبيق الثنائية (ABI)

إنّ ثبات واجهة التطبيق الثنائية (ABI) هو شرط أساسي لتطبيق التحديثات المتعلقة بالإطار فقط، لأنّ وحدات المورّدين قد تعتمد على مكتبات Vendor Native Development Kit (VNDK) المشترَكة التي تقع في قسم النظام. ضمن إصدار Android، يجب أن تكون مكتبات VNDK المشترَكة المنشأة حديثًا متوافقة مع ABI مع مكتبات VNDK المشترَكة التي تم إصدارها سابقًا حتى تتمكّن وحدات المورّدين من العمل مع هذه المكتبات بدون إعادة الترجمة وبدون أخطاء وقت التشغيل. يمكن تغيير مكتبات VNDK بين إصدارات Android، ولا تتوفّر أي ضمانات بشأن ABI.

للمساعدة في ضمان توافق ABI، يتضمّن الإصدار 9 من Android أداة تحقّق من 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++ المقابل.
    • خفض مستوى محدد الوصول***

* يجب عدم تغيير وظائف الأعضاء العامة والخاصة أو إزالتها لأنّ الوظائف المضمّنة العامة يمكن أن تشير إلى وظائف الأعضاء الخاصة. يمكن الاحتفاظ بمراجع الرموز إلى الدوال الخاصة للأعضاء في ملفات الثنائيات للمتصل. إنّ تغيير وظائف الأعضاء الخاصة أو إزالتها من المكتبات المشتركة قد يؤدي إلى إنشاء ملفات ثنائية غير متوافقة مع الإصدارات السابقة.

** يجب عدم تغيير العناصر المرجعية لأعضاء البيانات العامة أو الخاصة لأنّ الدوالّ المضمّنة يمكنها الإشارة إلى أعضاء البيانات هؤلاء في نص الدالة. يمكن أن يؤدي تغيير Offsets لأعضاء البيانات إلى توليد ملفَي binary غير متوافقَين مع الإصدارات القديمة.

*** على الرغم من أنّ هذه التغييرات لا تغيّر تنسيق الذاكرة للنوع، إلا أنّ هناك اختلافات دلالية قد تؤدي إلى عدم عمل المكتبات على النحو المتوقّع.

استخدام أدوات الامتثال لـ 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:

  1. تعالج أداة header-abi-dumper ملفات المصدر المجمَّعة لإنشاء مكتبة VNDK (ملفات المصدر الخاصة بالمكتبة بالإضافة إلى ملفات المصدر المُكتسَبة من خلال الملحقات الثابتة الانتقالية)، وذلك لإنشاء ملفات .sdump التي تتوافق مع كل مصدر.
    إنشاء ملف sdump
    الشكل 1. إنشاء ملفات .sdump
  2. بعد ذلك، تعالج أداة header-abi-linker ملفات .sdump (باستخدام نص برمجي للإصدار مقدَّم لها أو ملف .so المقابل للمكتبة المشتركة) لإنشاء ملف .lsdump يسجِّل جميع معلومات ABI المقابلة للمكتبة المشتركة.
    إنشاء lsdump
    الشكل 2. إنشاء ملف .lsdump
  3. تقارن أداة header-abi-diff ملف .lsdump بملف .lsdump مرجعي لإنشاء تقرير اختلاف يوضّح الاختلافات في واجهات برمجة التطبيقات للمكتبتَين.
    إنشاء ملف abi diff
    الشكل 3. إنشاء تقرير الاختلافات

header-abi-dumper

تُحلِّل أداة header-abi-dumper ملف مصدر C/C++ وتُسجِّل ABI المستنِدة من ملف المصدر هذا في ملف وسيط. يُشغِّل نظام الإنشاء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 التي تصدّرها الرؤوس العامة في directory 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 من المكتبة المشتركة
  • الأدلة المضمّنة في التصدير
الإخراج ملف يصف واجهة برمجة التطبيقات لنظام التشغيل لإحدى المكتبات المشتركة (على سبيل المثال، 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 بين المكتبتَين.

مداخل
  • ملف .lsdump يمثّل ABI لمكتبة قديمة مشترَكة
  • ملف .lsdump يمثّل ABI لمكتبة مشتركة جديدة
الإخراج تقرير اختلاف يوضّح الاختلافات في واجهات برمجة التطبيقات الأساسية التي تقدّمها المَرحلتَان المشترَكتَان المُقارَنتان

ملف الاختلافات في ABI بتنسيق protobuf text format يخضع التنسيق للتغيير في الإصدارات المستقبلية.

على سبيل المثال، لديك نسختان من 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