پایداری رابط دودویی برنامه (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