יציבות ABI

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

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

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

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

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

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

  • מיוצאים על ידי הכותרות הציבוריות של ספרייה משותפת.
  • מופיע בטבלה .dynsym בקובץ .so שמתאים לספרייה המשותפת.
  • יש לו קשירה מסוג WEAK או GLOBAL.
  • החשיפה היא DEFAULT או PROTECTED.
  • אינדקס הקטע לא מוגדר.
  • הסוג הוא 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

צריך לוודא שהספריות שמסומנות ב-vendor_available: true וב-vndk.enabled: true בקבצים המתאימים של Android.bp תואמות ל-ABI. לדוגמה:

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, ביומן הבנייה מוצגות אזהרות עם סוג האזהרה ונתיב לדוח 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 כדי ליצור דוח השוואה שמפרט את ההבדלים בממשקי ה-ABI של שתי הספריות.
    יצירת הפרש של ABI
    איור 3. יצירת דוח ההשוואה

header-abi-dumper

הכלי header-abi-dumper מנתח קובץ מקור של C/C++‎ ומייצא את ה-ABI שהוסק מקובץ המקור הזה לקובץ ביניים. מערכת ה-build מריצה את header-abi-dumper על כל קובצי המקור שעברו קומפילציה, וגם יוצרת ספרייה שכוללת את קובצי המקור מיחסי תלות טרנזיטיביים.

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

כרגע קובצי .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, ואז מקשר בין הקבצים האלה:

כניסות קלט
  • קבצים זמניים שנוצרו על ידי header-abi-dumper
  • קובץ סקריפט/מפה של הגרסה (אופציונלי)
  • קובץ אחד (.so) של הספרייה המשותפת
  • ספריות כלולות שיוצאו
פלט קובץ שמתאר את ה-ABI של ספרייה משותפת (לדוגמה, libfoo.so.lsdump מייצג את ה-ABI של libfoo).

הכלי ממזג את הגרפים של הטיפוסים בכל הקבצים הזמניים שמועברים אליו, תוך התחשבות בהבדלים של הגדרה אחת (יכול להיות שטיפוסים שהוגדרו על ידי המשתמש ביחידות תרגום שונות עם אותו שם מלא יהיו שונים מבחינה סמנטית) בין יחידות התרגום. הכלי מנתח סקריפט של גרסה או את טבלת הספרייה המשותפת (קובץ .so) כדי ליצור רשימה של הסמלים המיוצאים..dynsym

לדוגמה, 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 dump הסופי שנוצר מ-libfoo.so.

header-abi-diff

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

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

קובץ ה-diff של ה-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 מכיל דוח של כל השינויים שגורמים לשבירת תאימות של ABI ב-libfoo. ההודעה record_type_diffs מציינת שרשומה השתנתה ומפרטת את השינויים הלא תואמים, כולל:

  • גודל הרשומה השתנה מ-24 בייטים ל-8 בייטים.
  • סוג השדה של mfoo השתנה מ-foo ל-foo * (כל ההגדרות של typedef הוסרו).

השדה 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