יציבות ABI

יציבות של ממשק בינארי של אפליקציה (ABI) היא תנאי מוקדם לעדכונים של מסגרת בלבד, כי מודולים של ספקים עשויים להיות תלויים בספריות המשותפות של Vendor Native Development Kit‏ (VNDK) שנמצאות במחיצה של המערכת. בגרסה של Android, ספריות משותפות של VNDK שפותחו לאחרונה צריכות להיות תואמות ל-ABI לספריות משותפות של VNDK שהושקו בעבר, כדי שהמודולים של ספקים יוכלו לעבוד עם הספריות האלה ללא הידור מחדש וללא שגיאות בזמן הריצה. בין גרסאות Android, יכול להיות שיחולו שינויים בספריות VNDK, ואין garanties של ABI.

כדי להבטיח תאימות ל-ABI, גרסת Android 9 כוללת בדיקת ABI של כותרות, כפי שמתואר בקטעים הבאים.

מידע על תאימות ל-VNDK ול-ABI

VNDK היא קבוצה מוגבלת של ספריות שמודולים של ספקים יכולים לקשר אליהן, ומאפשרת עדכונים של מסגרת בלבד. תאימות ABI מתייחסת ליכולת של גרסה חדשה יותר של ספרייה משותפת לפעול כצפוי עם מודול שמקושר אליה באופן דינמי (כלומר, פועלת כמו גרסה ישנה יותר של הספרייה).

מידע על סמלים שיוצאו

סמל שיוצא (נקרא גם סמל גלובלי) הוא סמל שעומד בכל הדרישות הבאות:

  • מיוצאים על ידי כותרות הציבוריות של ספרייה משותפת.
  • מופיע בטבלה .dynsym בקובץ .so שמתאים לספרייה המשותפת.
  • יש לו קישור WEAK או GLOBAL.
  • החשיפה היא 'ברירת מחדל' או 'מוגן'.
  • אינדקס הקטע הוא לא UNDEFINED.
  • הערך של Type הוא 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 מיוצא דרך foo_exported.h דרך bar_t *.

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 ברמת API 27, מתבצעת השוואה בין ה-ABI המשוער של libfoo לבין ההפניה שלו ב:

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

שגיאה ב-ABI

במקרה של שגיאות ABI, ביומן ה-build יוצגו אזהרות עם סוג האזהרה ונתיב לדוח abi-diff. לדוגמה, אם ב-ABI של libbinder יש שינוי לא תואם, מערכת ה-build גורמת לשגיאה עם הודעה שדומה לזו:

*****************************************************
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 שהוסקו מקובץ המקור הזה לקובץ ביניים. מערכת ה-build מפעילה את header-abi-dumper בכל קובצי המקור שעברו הידור, תוך כדי בניית ספרייה שכוללת את קובצי המקור מיחסי תלות טרנזיים.

כניסות קלט
  • קובץ מקור של C/C++‎
  • ספריות include שיוצאו
  • דגלים של מהדר
פלט קובץ שמתאר את ה-ABI של קובץ המקור (לדוגמה, foo.sdump מייצג את ה-ABI של foo.cpp).

נכון לעכשיו, קבצים מסוג .sdump הם בפורמט JSON, ולא מובטח שהם יהיו יציבים בגרסאות עתידיות. לכן, צריך להתייחס לעיצוב הקובץ .sdump כפרט בהטמעה של מערכת ה-build.

לדוגמה, לקובץ 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. המידע הזה מאפשר לבצע השוואה חזרה (recursive diffing).
  • functions. ייצוג של פונקציות שיוצאו על ידי כותרות ציבוריות. יש בהם גם מידע על השם הפגום של הפונקציה, סוג ההחזרה, סוגי הפרמטרים, מציין הגישה ומאפיינים אחרים.

header-abi-linker

הכלי header-abi-linker מקבל את הקבצים הביניים שנוצרו על ידי header-abi-dumper כקלט, ולאחר מכן מקשר את הקבצים האלה:

כניסות קלט
  • קובצי ביניים שנוצרו על ידי header-abi-dumper
  • קובץ המפה/סקריפט הגרסה (אופציונלי)
  • קובץ .so של הספרייה המשותפת
  • הושלם הייצוא של ספריות מסוג 'הכללה'
פלט קובץ שמתאר את ה-ABI של ספרייה משותפת (לדוגמה, libfoo.so.lsdump מייצג את ה-ABI של 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 הוא קובץ ה-dump הסופי של ABI שנוצר מ-libfoo.so.

header-abi-diff

הכלי header-abi-diff משווה בין שני קובצי .lsdump שמייצגים את ה-ABI של שתי ספריות, ומפיק דוח הבדלים בין שני ממשקי ה-ABI.

כניסות קלט
  • קובץ .lsdump שמייצג את ה-ABI של ספרייה משותפת ישנה.
  • קובץ .lsdump שמייצג את ה-ABI של ספרייה משותפת חדשה.
פלט דוח diff שמציג את ההבדלים ב-ABIs שמוצעים בשתי הספריות המשותפות שנערכת ביניהם השוואה.

קובץ ההבדלים של ABI הוא ב פורמט טקסט של 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 * (כל ה-typedefs מוסרים).

השדה 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 גורם עכשיו לשגיאת build.

כדי לעדכן את ההפניות ל-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