پایداری رابط دودویی برنامه (ABI) پیش نیاز بهروزرسانیهای فقط چارچوبی است، زیرا ماژولهای فروشنده ممکن است به کتابخانههای اشتراکگذاری کیت توسعه بومی فروشنده (VNDK) وابسته باشند که در پارتیشن سیستم قرار دارند. در نسخه اندروید، کتابخانه های مشترک VNDK تازه ساخته شده باید با کتابخانه های مشترک VNDK قبلا منتشر شده سازگار باشد تا ماژول های فروشنده بتوانند بدون کامپایل مجدد و بدون خطا در زمان اجرا با آن کتابخانه ها کار کنند. بین نسخههای اندروید، کتابخانههای VNDK قابل تغییر هستند و هیچ ضمانت ABI وجود ندارد.
برای کمک به اطمینان از سازگاری ABI، Android 9 شامل یک جستجوگر هدر ABI است که در بخشهای زیر توضیح داده شده است.
درباره انطباق VNDK و ABI
VNDK مجموعهای محدود از کتابخانهها است که ماژولهای فروشنده ممکن است به آن پیوند دهند و بهروزرسانیهای فقط چارچوب را فعال میکنند. انطباق ABI به توانایی یک نسخه جدیدتر از یک کتابخانه مشترک برای کار همانطور که انتظار می رود با ماژولی که به صورت پویا به آن مرتبط است (یعنی مانند نسخه قدیمی کتابخانه کار می کند) اشاره دارد.
درباره نمادهای صادراتی
نماد صادراتی (همچنین به عنوان نماد جهانی شناخته می شود) به نمادی اشاره دارد که تمام موارد زیر را برآورده می کند:
- صادر شده توسط سرصفحه های عمومی یک کتابخانه مشترک.
- در جدول
.dynsymفایل.soمربوط به کتابخانه مشترک ظاهر می شود. - دارای اتصال ضعیف یا جهانی است.
- قابلیت مشاهده پیشفرض یا محافظت شده است.
- شاخص بخش تعریف نشده نیست.
- نوع یا 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 اطمینان حاصل کنید
برای کتابخانههایی که vendor_available: true و vndk.enabled: true در فایلهای Android.bp مربوطه دارند، باید از انطباق ABI اطمینان حاصل شود. به عنوان مثال:
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
برای انواع دادههایی که بهطور مستقیم یا غیرمستقیم توسط یک تابع صادر شده قابل دسترسی هستند، تغییرات زیر در یک کتابخانه بهعنوان ABI-breaking طبقهبندی میشوند:
| نوع داده | توضیحات |
|---|---|
| سازه ها و کلاس ها |
|
| اتحادیه ها |
|
| شمارش ها |
|
| نمادهای جهانی |
|
* توابع عضو عمومی و خصوصی نباید تغییر یا حذف شوند زیرا توابع درون خطی عمومی می توانند به توابع عضو خصوصی اشاره کنند. ارجاعات نماد به توابع اعضای خصوصی را می توان در باینری های تماس گیرنده نگه داشت. تغییر یا حذف توابع اعضای خصوصی از کتابخانه های مشترک می تواند منجر به باینری های ناسازگار با عقب گرد شود.
** جایگزینی اعضای داده عمومی یا خصوصی نباید تغییر کند زیرا توابع درون خطی می توانند به این اعضای داده در بدنه عملکرد خود اشاره کنند. تغییر افست اعضای داده می تواند منجر به باینری های ناسازگار با عقب شود.
*** در حالی که اینها چیدمان حافظه نوع را تغییر نمیدهند، تفاوتهای معنایی وجود دارد که میتواند منجر به کار نکردن کتابخانهها طبق انتظار شود.
از ابزارهای سازگاری ABI استفاده کنید
هنگامی که یک کتابخانه VNDK ساخته می شود، ABI کتابخانه با مرجع ABI مربوطه برای نسخه VNDK در حال ساخت مقایسه می شود. محل های رفرنس ABI در موارد زیر قرار دارند:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
به عنوان مثال، در ساخت libfoo برای x86 در سطح API 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مقایسه می کند تا گزارشی از تفاوت ایجاد کند که تفاوت های موجود در ABI های دو کتابخانه را مشخص می کند.
شکل 3. ایجاد گزارش تفاوت
header-abi-dumper
ابزار header-abi-dumper یک فایل منبع C/C++ را تجزیه می کند و ABI استنباط شده از آن فایل منبع را در یک فایل میانی تخلیه می کند. سیستم ساخت header-abi-dumper روی همه فایلهای منبع کامپایل شده اجرا میکند و در عین حال کتابخانهای میسازد که شامل فایلهای منبع از وابستگیهای انتقالی است.
| ورودی ها |
|
|---|---|
| خروجی | فایلی که 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 را که توسط سربرگهای عمومی در فهرست 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 به عنوان ورودی می گیرد و سپس آن فایل ها را پیوند می دهد:
| ورودی ها |
|
|---|---|
| خروجی | فایلی که ABI یک کتابخانه مشترک را توصیف می کند (برای مثال libfoo.so.lsdump نشان دهنده ABI libfoo است). |
این ابزار، نمودارهای نوع را در همه فایلهای میانی دادهشده به آن، با در نظر گرفتن تفاوتهای یک تعریف (انواع تعریفشده توسط کاربر در واحدهای ترجمه مختلف با نام کاملاً واجد شرایط یکسان، ممکن است از نظر معنایی متفاوت باشد) در واحدهای ترجمه ادغام میکند. سپس این ابزار یک اسکریپت نسخه یا جدول .dynsym کتابخانه مشترک (فایل .so ) را تجزیه می کند تا فهرستی از نمادهای صادر شده ایجاد کند.
برای مثال، libfoo از foo.cpp و bar.cpp تشکیل شده است. header-abi-linker می توان برای ایجاد کامل ABI dump پیوندی 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 های ارائه شده توسط دو کتابخانه مشترک را در مقایسه با یکدیگر بیان می کند. |
فایل تفاوت ABI در قالب متن پروتوباف است. قالب ممکن است در نسخه های بعدی تغییر کند.
به عنوان مثال، شما دو نسخه از 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 و API را اجرا کنید
برای اعمال ABI و API کتابخانه های مشترک 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>
به عنوان مثال، برای به روز رسانی مراجع ABI libbinder ، اجرا کنید:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder