ABI की स्थिरता

सिर्फ़ फ़्रेमवर्क अपडेट के लिए, ऐप्लिकेशन बाइनरी इंटरफ़ेस (एबीआई) का स्टेबल होना ज़रूरी है. ऐसा इसलिए, क्योंकि वेंडर मॉड्यूल, सिस्टम पार्टिशन में मौजूद वेंडर नेटिव डेवलपमेंट किट (वीएनडीके) की शेयर की गई लाइब्रेरी पर निर्भर हो सकते हैं. Android के किसी वर्शन में, नई वीएनडीके शेयर की गई लाइब्रेरी, पहले रिलीज़ की गई वीएनडीके शेयर की गई लाइब्रेरी के साथ एबीआई के हिसाब से काम करनी चाहिए. इससे वेंडर मॉड्यूल, उन लाइब्रेरी के साथ बिना फिर से कंपाइल किए और बिना रनटाइम की गड़बड़ियों के काम कर सकते हैं. Android के रिलीज़ के बीच, VNDK लाइब्रेरी बदली जा सकती हैं. साथ ही, एबीआई की कोई गारंटी नहीं होती.

एबीआई के साथ काम करने की सुविधा को पक्का करने के लिए, Android 9 में हेडर एबीआई की जांच करने वाला टूल शामिल है. इसके बारे में यहां बताया गया है.

वीएनडीके और एबीआई के नियमों का पालन करने के बारे में जानकारी

वीएनडीके, लाइब्रेरी का एक ऐसा सेट है जिस पर पाबंदी लगी होती है. वेंडर मॉड्यूल, इससे लिंक हो सकते हैं. साथ ही, इससे सिर्फ़ फ़्रेमवर्क को अपडेट किया जा सकता है. एबीआई के मुताबिक होना का मतलब है कि शेयर की गई लाइब्रेरी का नया वर्शन, उससे डाइनैमिक तरीके से लिंक किए गए मॉड्यूल के साथ उम्मीद के मुताबिक काम कर सकता है. इसका मतलब है कि यह लाइब्रेरी के पुराने वर्शन की तरह काम करता है.

एक्सपोर्ट किए गए सिंबल के बारे में जानकारी

एक्सपोर्ट किया गया सिंबल (इसे ग्लोबल सिंबल भी कहा जाता है) उस सिंबल को कहते हैं जो इन सभी शर्तों को पूरा करता है:

  • इन्हें शेयर की गई लाइब्रेरी के पब्लिक हेडर से एक्सपोर्ट किया जाता है.
  • शेयर की गई लाइब्रेरी से जुड़ी .so फ़ाइल की .dynsym टेबल में दिखता है.
  • इसमें 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 में बदलाव करने की अनुमति है.)

इसी तरह का ब्यौरा, बेस क्लास स्पेसिफ़ायर और टेंप्लेट पैरामीटर के ज़रिए ऐक्सेस किए जा सकने वाले टाइप के लिए भी दिया जा सकता है.

पक्का करें कि एबीआई का पालन किया गया हो

Android.bp फ़ाइलों में, vendor_available: true और vndk.enabled: true के तौर पर मार्क की गई लाइब्रेरी के लिए, ABI का पालन करना ज़रूरी है. उदाहरण के लिए:

cc_library {
    name: "libvndk_example",
    vendor_available: true,
    vndk: {
        enabled: true,
    }
}

एक्सपोर्ट किए गए फ़ंक्शन से सीधे या किसी दूसरे तरीके से ऐक्सेस किए जा सकने वाले डेटा टाइप के लिए, लाइब्रेरी में किए गए इन बदलावों को ABI-ब्रेकिंग के तौर पर क्लासिफ़ाई किया जाता है:

डेटा टाइप ब्यौरा
स्ट्रक्चर और क्लास
  • क्लास टाइप या स्ट्रक्चर टाइप का साइज़ बदलें.
  • बेसिक क्लास
    • बेस क्लास जोड़ें या हटाएं.
    • वर्चुअली इनहेरिट की गई बेस क्लास जोड़ें या हटाएं.
    • बेस क्लास का क्रम बदलें.
  • सदस्यता वाले फ़ंक्शन
    • सदस्यता से जुड़ी सुविधाएं हटा दी गई हैं*.
    • सदस्य फ़ंक्शन में आर्ग्युमेंट जोड़ें या हटाएं.
    • सदस्यता वाले फ़ंक्शन* के आर्ग्युमेंट टाइप या रिटर्न टाइप बदलें.
    • वर्चुअल टेबल का लेआउट बदलें.
  • डेटा मेंबर
    • स्टैटिक डेटा मेंबर हटाएं.
    • नॉन-स्टैटिक डेटा मेंबर जोड़ें या हटाएं.
    • डेटा सदस्यों के टाइप बदलें.
    • ऑफ़सेट को नॉन-स्टैटिक डेटा मेंबर** में बदलें.
    • डेटा सदस्यों के const, volatile, और/या restricted क्वालिफ़ायर*** बदलें.
    • डेटा सदस्यों के ऐक्सेस स्पेसिफ़ायर को डाउनग्रेड करें***.
  • टेंप्लेट के तर्कों में बदलाव करें.
यूनियन
  • डेटा सदस्यों को जोड़ें या हटाएं.
  • यूनियन टाइप का साइज़ बदलें.
  • डेटा सदस्यों के टाइप बदलें.
इन्यूमरेशन
  • मौजूदा टाइप बदलें.
  • गिनती करने वालों के नाम बदलें.
  • एन्यूमरेटर की वैल्यू बदलें.
ग्लोबल सिंबल
  • सार्वजनिक हेडर से एक्सपोर्ट किए गए सिंबल हटाएं.
  • FUNC टाइप के ग्लोबल सिंबल के लिए
    • आर्ग्युमेंट जोड़ें या हटाएं.
    • आर्ग्युमेंट के टाइप बदलें.
    • लौटाने का टाइप बदलें.
    • ऐक्सेस स्पेसिफ़ायर को डाउनग्रेड करें***.
  • OBJECT टाइप के ग्लोबल सिंबल के लिए
    • C/C++ टाइप को बदलें.
    • ऐक्सेस स्पेसिफ़ायर को डाउनग्रेड करें***.

* सार्वजनिक और निजी, दोनों तरह के सदस्य फ़ंक्शन में बदलाव नहीं किया जाना चाहिए और न ही उन्हें हटाया जाना चाहिए. ऐसा इसलिए, क्योंकि सार्वजनिक इनलाइन फ़ंक्शन, निजी सदस्य फ़ंक्शन का रेफ़रंस दे सकते हैं. निजी सदस्य फ़ंक्शन के सिंबल रेफ़रंस, कॉलर बाइनरी में रखे जा सकते हैं. शेयर की गई लाइब्रेरी से, सदस्यों के लिए उपलब्ध फ़ंक्शन में बदलाव करने या उन्हें हटाने से, बाइनरी फ़ाइलें पहले की तरह काम नहीं करेंगी.

** सार्वजनिक या निजी डेटा सदस्यों के ऑफ़सेट में बदलाव नहीं किया जाना चाहिए, क्योंकि इनलाइन फ़ंक्शन, अपने फ़ंक्शन बॉडी में इन डेटा सदस्यों का रेफ़रंस दे सकते हैं. डेटा मेंबर ऑफ़सेट बदलने से, बाइनरी के पिछले वर्शन के साथ काम न करने वाली बाइनरी बन सकती हैं.

*** इनसे टाइप के मेमोरी लेआउट में कोई बदलाव नहीं होता है. हालांकि, सिमैंटिक अंतर होते हैं. इनकी वजह से, लाइब्रेरी उम्मीद के मुताबिक काम नहीं कर सकती हैं.

एबीआई के मुताबिक काम करने वाले टूल का इस्तेमाल करना

जब कोई वीएनडीके लाइब्रेरी बनाई जाती है, तब लाइब्रेरी के एबीआई की तुलना, बनाए जा रहे वीएनडीके के वर्शन के एबीआई रेफ़रंस से की जाती है. रेफ़रंस एबीआई डंप यहां मौजूद हैं:

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based

उदाहरण के लिए, एपीआई लेवल 27 पर x86 के लिए libfoo बनाते समय, libfoo के अनुमानित ABI की तुलना, यहां दिए गए रेफ़रंस से की जाती है:

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump

एबीआई के काम न करने की गड़बड़ी

एबीआई में बदलाव होने पर, बिल्ड लॉग में चेतावनियां दिखती हैं. इनमें चेतावनी का टाइप और abi-diff रिपोर्ट का पाथ होता है. उदाहरण के लिए, अगर libbinder के ABI में कोई ऐसा बदलाव किया गया है जो काम नहीं करता है, तो बिल्ड सिस्टम गड़बड़ी का मैसेज दिखाता है. यह मैसेज कुछ इस तरह का होता है:

*****************************************************
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 ----

वीएनडीके लाइब्रेरी के एबीआई की जांच करना

वीएनडीके लाइब्रेरी बनाने पर:

  1. header-abi-dumper, वीएनडीके लाइब्रेरी बनाने के लिए कंपाइल की गई सोर्स फ़ाइलों को प्रोसेस करता है. इनमें लाइब्रेरी की अपनी सोर्स फ़ाइलें और स्टैटिक ट्रांज़िटिव डिपेंडेंसी से इनहेरिट की गई सोर्स फ़ाइलें शामिल होती हैं. इससे, .sdump फ़ाइलें जनरेट होती हैं, जो हर सोर्स से जुड़ी होती हैं.
    sdump बनाना
    पहली इमेज. .sdump फ़ाइलें बनाई जा रही हैं
  2. इसके बाद, header-abi-linker, .sdump फ़ाइलों को प्रोसेस करता है. इसके लिए, वह या तो उसे दी गई वर्शन स्क्रिप्ट का इस्तेमाल करता है या शेयर की गई लाइब्रेरी से जुड़ी .so फ़ाइल का इस्तेमाल करता है. इससे .lsdump फ़ाइल बनती है. इस फ़ाइल में, शेयर की गई लाइब्रेरी से जुड़ी सभी एबीआई जानकारी लॉग होती है.
    lsdump बनाना
    दूसरी इमेज. .lsdump फ़ाइल बनाई जा रही है
  3. header-abi-diff, .lsdump फ़ाइल की तुलना, पहचान वाली .lsdump फ़ाइल से करता है. इससे एक अंतर रिपोर्ट जनरेट होती है. इस रिपोर्ट में, दोनों लाइब्रेरी के एबीआई में मौजूद अंतर के बारे में बताया जाता है.
    एबीआई में अंतर का पता लगाना
    तीसरी इमेज. डिफ़ रिपोर्ट बनाना

header-abi-dumper

header-abi-dumper टूल, C/C++ सोर्स फ़ाइल को पार्स करता है. साथ ही, उस सोर्स फ़ाइल से अनुमानित एबीआई को इंटरमीडिएट फ़ाइल में डंप करता है. बिल्ड सिस्टम, कंपाइल की गई सभी सोर्स फ़ाइलों पर header-abi-dumper को चलाता है. साथ ही, यह एक ऐसी लाइब्रेरी भी बनाता है जिसमें ट्रांज़िटिव डिपेंडेंसी की सोर्स फ़ाइलें शामिल होती हैं.

इनपुट
  • C/C++ सोर्स फ़ाइल
  • एक्सपोर्ट की गई फ़ाइलों में शामिल डायरेक्ट्री
  • कंपाइलर फ़्लैग
आउटपुट ऐसी फ़ाइल जिसमें सोर्स फ़ाइल के एबीआई के बारे में जानकारी होती है. उदाहरण के लिए, 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 को पार्स करे. साथ ही, exported डायरेक्ट्री में मौजूद सार्वजनिक हेडर से एक्सपोर्ट की गई एबीआई की जानकारी को दिखाए. यहां दिया गया जवाब, header-abi-dumper ने जनरेट किया है. foo.sdump

{
 "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 में सोर्स फ़ाइल foo.cpp से एक्सपोर्ट की गई एबीआई की जानकारी और सार्वजनिक हेडर शामिल होते हैं. उदाहरण के लिए,

  • record_types. सार्वजनिक हेडर में तय किए गए स्ट्रक्चर, यूनियन या क्लास देखें. हर रिकॉर्ड टाइप में, उसके फ़ील्ड, साइज़, ऐक्सेस स्पेसिफ़ायर, हेडर फ़ाइल, और अन्य एट्रिब्यूट के बारे में जानकारी होती है.
  • pointer_types. सार्वजनिक हेडर में एक्सपोर्ट किए गए रिकॉर्ड/फ़ंक्शन से सीधे/अप्रत्यक्ष तौर पर रेफ़र किए गए पॉइंटर टाइप देखें. साथ ही, पॉइंटर जिस टाइप की ओर इशारा करता है उसे देखें. इसके लिए, type_info में मौजूद referenced_type फ़ील्ड का इस्तेमाल करें. इसी तरह की जानकारी, .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 को इस तरह से कॉल किया जा सकता है, ताकि libfoo का पूरा लिंक किया गया ABI डंप बनाया जा सके:

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) को लिंक करती है. साथ ही, डायरेक्ट्री exported में मौजूद हेडर में मौजूद ABI की जानकारी को फ़िल्टर करती है.
  • यह कुकी libfoo.so को पार्स करती है. साथ ही, लाइब्रेरी की .dynsym टेबल के ज़रिए एक्सपोर्ट किए गए सिंबल के बारे में जानकारी इकट्ठा करती है.
  • _Z3FooiP3bar और _Z6FooBadiP3foo को जोड़ता है.

libfoo.so.lsdump, libfoo.so का जनरेट किया गया फ़ाइनल एबीआई डंप है.

header-abi-diff

header-abi-diff टूल, दो .lsdump फ़ाइलों की तुलना करता है. ये फ़ाइलें, दो लाइब्रेरी के ABI को दिखाती हैं. साथ ही, यह एक अंतर वाली रिपोर्ट जनरेट करता है. इस रिपोर्ट में, दोनों ABI के बीच के अंतर के बारे में बताया जाता है.

इनपुट
  • .lsdump फ़ाइल, शेयर की गई पुरानी लाइब्रेरी के ABI के बारे में बताती है.
  • .lsdump फ़ाइल, नई शेयर की गई लाइब्रेरी के ABI के बारे में बताती है.
आउटपुट डिफ़ रिपोर्ट, जिसमें तुलना की गई दो शेयर की गई लाइब्रेरी के एबीआई में अंतर बताया गया हो.

एबीआई डिफ़ फ़ाइल, protobuf टेक्स्ट फ़ॉर्मैट में होती है. आने वाली रिलीज़ में, इस फ़ॉर्मैट में बदलाव हो सकता है.

उदाहरण के लिए, आपके पास libfoo के दो वर्शन हैं: libfoo_old.so और libfoo_new.so. libfoo_new.so में, bar_t में जाकर, mfoo के टाइप को foo_t से बदलकर foo_t * करें. bar_t एक ऐक्सेस किया जा सकने वाला टाइप है. इसलिए, header-abi-diff को इसे ABI में होने वाले बदलाव के तौर पर फ़्लैग करना चाहिए.

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 में, libfoo में हुए सभी एबीआई ब्रेकिंग बदलावों की रिपोर्ट शामिल होती है. record_type_diffs मैसेज से पता चलता है कि किसी रिकॉर्ड में बदलाव किया गया है. इसमें उन बदलावों की सूची दी गई होती है जो काम नहीं करते. इनमें ये शामिल हैं:

  • रिकॉर्ड का साइज़ 24 बाइट से बदलकर 8 बाइट हो गया है.
  • mfoo के फ़ील्ड टाइप को foo से बदलकर foo * किया गया है. इसमें सभी टाइपडेफ़ हटा दिए गए हैं.

type_stack फ़ील्ड से पता चलता है कि header-abi-diff, bar में कैसे पहुंचा. इस फ़ील्ड को इस तरह से समझा जा सकता है कि Foo एक एक्सपोर्ट किया गया फ़ंक्शन है, जो bar * को पैरामीटर के तौर पर लेता है. यह bar की ओर इशारा करता है, जिसे एक्सपोर्ट किया गया था और जिसमें बदलाव किया गया था.

एबीआई और एपीआई लागू करना

वीएनडीके की शेयर की गई लाइब्रेरी के एबीआई और एपीआई को लागू करने के लिए, एबीआई रेफ़रंस को ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/ में चेक इन करना ज़रूरी है. इन रेफ़रंस को बनाने के लिए, यह कमांड चलाएं:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py

रेफ़रंस बनाने के बाद, सोर्स कोड में किया गया कोई भी ऐसा बदलाव जिसकी वजह से वीएनडीके लाइब्रेरी में एबीआई/एपीआई में बदलाव होता है, अब बिल्ड से जुड़ी गड़बड़ी के तौर पर दिखता है.

कुछ लाइब्रेरी के लिए, ABI रेफ़रंस अपडेट करने के लिए, यह कमांड चलाएं:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>

उदाहरण के लिए, libbinder के एबीआई रेफ़रंस अपडेट करने के लिए, यह कमांड चलाएं:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder