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

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

للمساعدة في ضمان التوافق مع واجهات التطبيق الثنائية (ABI)، يتضمّن الإصدار 9 من Android أداة فحص واجهة التطبيق الثنائية (ABI) كما هو موضّح في الأقسام التالية.

لمحة عن امتثال VNDK وABI

‫VNDK هي مجموعة مفروض عليها قيود من المكتبات التي يمكن أن ترتبط بها وحدات المورّدين، والتي تتيح التحديثات للإطار فقط. يشير التوافق مع ABI إلى قدرة إصدار أحدث من مكتبة مشترَكة على العمل على النحو المتوقّع مع ملف برمجي مرتبط به ديناميكيًا (أي يعمل بالطريقة نفسها التي يعمل بها إصدار أقدم من مكتبة ).

لمحة عن الرموز التي تم تصديرها

يشير الرمز الذي تم تصديره (المعروف أيضًا باسم الرمز الشامل) إلى رمز يستوفي جميع الشروط التالية:

  • يتم تصديرها من خلال الرؤوس العامة لمكتبة مشتركة.
  • يظهر في جدول .dynsym لملف .so المقابل للمكتبة المشتركة.
  • أن يكون لها ربط WEAK أو GLOBAL
  • يكون مستوى العرض "تلقائي" أو "محمي".
  • لم يتم تعريف فهرس القسم.
  • يكون النوع إما 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 corresponding المعنيَّين. مثلاً:

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 في 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) للمكتبتين.
    إنشاء ملف abi diff
    الشكل 3. إنشاء تقرير الاختلافات

header-abi-dumper

تحلّل أداة header-abi-dumper ملف المصدر C/C++ وتجمع واجهة ABI المستنتَجة من ملف المصدر هذا في ملف وسيط. يشغِّل نظام التصميم header-abi-dumper على كل ملفات المصدر المجمّعة، مع إنشاء مكتبة تتضمّن ملفات المصدر من اعتماديات انتقالية.

مداخل
  • ملف مصدر C/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 التي تصدّرها الرؤوس العامة في 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) التي توفّرها المكتبتان المشتركتان مقارنةً بهما.

ملف الاختلافات في ABI بتنسيق تنسيق نص 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