ثبات ABI

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