יציבות ABI

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

כדי לעזור להבטיח תאימות ABI, אנדרואיד 9 כולל בודק כותרת ABI, כמתואר בסעיפים הבאים.

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

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

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

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

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

ניתן לתת הסבר דומה גם לסוגים שניתן להגיע אליהם באמצעות מפרטי מחלקות בסיס ופרמטרים של תבניות.

הבטחת תאימות ABI

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

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

עבור סוגי נתונים שניתן להגיע אליהם ישירות או בעקיפין על ידי פונקציה מיוצאת, השינויים הבאים בספרייה מסווגים כשבירת ABI:

סוג מידע תיאור
מבנים וכיתות
  • שנה את הגודל של סוג המחלקה או סוג ה-struct.
  • שיעורי בסיס
    • הוסף או הסר מחלקות בסיס.
    • הוסף או הסר מחלקות בסיס כמעט בירושה.
    • שנה את סדר מחלקות הבסיס.
  • פונקציות חבר
    • הסר פונקציות חבר*.
    • הוסף או הסר ארגומנטים מפונקציות חבר.
    • שנה את סוגי הארגומנטים או סוגי ההחזרה של פונקציות איברים*.
    • שנה את פריסת הטבלה הוירטואלית.
  • חברי נתונים
    • הסר חברי נתונים סטטיים.
    • הוסף או הסר חברי נתונים שאינם סטטיים.
    • שנה את סוגי חברי הנתונים.
    • שנה את ההסטות לאברי נתונים שאינם סטטיים**.
    • שנה את ה- 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 creation
    איור 1. יצירת קבצי .sdump
  2. header-abi-linker לאחר מכן מעבד את קבצי .sdump (באמצעות סקריפט גרסה שסופק לו או קובץ ה- .so המתאים לספרייה המשותפת) כדי לייצר קובץ .lsdump שמתעד את כל מידע ה-ABI המתאים לספרייה המשותפת.
    lsdump creation
    איור 2. יצירת קובץ .lsdump
  3. header-abi-diff משווה את קובץ .lsdump עם קובץ .lsdump רפרנס כדי לייצר דוח diff שמתאר את ההבדלים ב-ABI של שתי הספריות.
    abi diff creation
    איור 3. יצירת דוח ההבדל

header-abi-dumper

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

תשומות
  • קובץ מקור AC/C++
  • מיוצא כולל ספריות
  • דגלי מהדר
תְפוּקָה קובץ שמתאר את ה-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 כקלט ואז מקשר את הקבצים האלה:

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

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

לדוגמה, libfoo מורכב מ- foo.cpp ו- bar.cpp . ניתן להפעיל header-abi-linker כדי ליצור את dump 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 של שתי ספריות ומפיק דוח diff המציין את ההבדלים בין שני ה-ABIs.

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

קובץ ABI diff הוא בפורמט טקסט 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 גורם כעת לשגיאת בנייה.

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