ثبات ABI

يُعدّ ثبات واجهة التطبيق الثنائية (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، ما يؤدي إلى تصدير المزيد من الأنواع:
  • 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++ المقابل.
    • خفض مستوى أداة تحديد الوصول***.

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

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

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

استخدام أدوات التوافق مع واجهة التطبيق الثنائية (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 مرجعي لإنشاء تقرير diff يوضّح الاختلافات في واجهات ABI للمكتبتَين.
    abi diff creation
    الشكل 3. إنشاء تقرير الاختلاف

header-abi-dumper

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

مداخل
  • ملف مصدر C/C++‎
  • تصدير أدلة التضمين
  • علامات برنامج التجميع
الإخراج ملف يصف واجهة التطبيق الثنائية (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 كمدخلات ثم تربط هذه الملفات:

مداخل
  • الملفات الوسيطة التي تم إنشاؤها بواسطة header-abi-dumper
  • نص الإصدار/ملف الخريطة (اختياري)
  • ملف .so الخاص بالمكتبة المشتركة
  • تصدير أدلة التضمين
الإخراج ملف يصف واجهة التطبيق الثنائية (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) لمكتبتَين، وتنتج تقريرًا يوضّح الاختلافات بين واجهتَي التطبيق الثنائية.

مداخل
  • ملف .lsdump يمثّل واجهة التطبيق الثنائية (ABI) لمكتبة مشتركة قديمة.
  • ملف .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