ABI Kararlılığı

Uygulama İkili Arabirimi (ABI) kararlılığı, satıcı modülleri, sistem bölümünde bulunan Satıcı Yerel Geliştirme Kiti (VNDK) paylaşılan kitaplıklarına bağlı olabileceğinden, yalnızca çerçeve güncelleştirmelerinin bir ön koşuludur. Bir Android sürümünde, yeni oluşturulmuş VNDK paylaşılan kitaplıklarının daha önce yayımlanmış VNDK paylaşılan kitaplıklarıyla ABI uyumlu olması gerekir, böylece satıcı modülleri bu kitaplıklarla yeniden derleme olmadan ve çalışma zamanı hatası olmadan çalışabilir. Android sürümleri arasında VNDK kitaplıkları değiştirilebilir ve ABI garantisi yoktur.

ABI uyumluluğunu sağlamaya yardımcı olmak için Android 9, aşağıdaki bölümlerde açıklandığı gibi bir başlık ABI denetleyicisi içerir.

VNDK ve ABI uyumluluğu hakkında

VNDK, satıcı modüllerinin bağlanabileceği ve yalnızca çerçeve güncellemelerini etkinleştiren kısıtlayıcı bir kitaplıklar kümesidir. ABI uyumluluğu , paylaşılan bir kitaplığın daha yeni bir sürümünün, kendisine dinamik olarak bağlı bir modülle beklendiği gibi çalışma yeteneğini ifade eder (yani kitaplığın eski bir sürümünün yapacağı gibi çalışır).

Dışa aktarılan semboller hakkında

Dışa aktarılan bir sembol ( genel sembol olarak da bilinir), aşağıdakilerin tümünü karşılayan bir sembole atıfta bulunur:

  • Paylaşılan bir kitaplığın genel üstbilgileri tarafından dışa aktarılır.
  • Paylaşılan kitaplığa karşılık gelen .so dosyasının .dynsym tablosunda görünür.
  • ZAYIF veya KÜRESEL bağlamaya sahiptir.
  • Görünürlük VARSAYILAN veya KORUMALIDIR.
  • Bölüm dizini TANIMLANMAMIŞ değil.
  • Tür, FUNC veya OBJECT'tir.

Paylaşılan bir kitaplığın genel başlıkları , paylaşılan kitaplığa karşılık gelen modülün Android.bp tanımlarında export_include_dirs , export_header_lib_headers , export_static_lib_headers , export_shared_lib_headers ve export_generated_headers öznitelikleri aracılığıyla diğer kitaplıklar/ikili dosyalar tarafından kullanılabilen başlıklar olarak tanımlanır.

Ulaşılabilir türler hakkında

Ulaşılabilir tür , dışa aktarılan bir sembol aracılığıyla doğrudan veya dolaylı olarak erişilebilen VE genel başlıklar aracılığıyla dışa aktarılan herhangi bir C/C++ yerleşik veya kullanıcı tanımlı türdür. Örneğin, libfoo.so , .dynsym tablosunda bulunan dışa aktarılmış bir sembol olan Foo işlevine sahiptir. libfoo.so kitaplığı şunları içerir:

foo_exported.h foo.özel.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 : [
    "include"
  ],
}
.dynsym tablosu
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 bakıldığında, doğrudan/dolaylı erişilebilir türler şunları içerir:

Tip Tanım
bool Foo dönüş türü.
int İlk Foo parametresinin türü.
bar_t * İkinci Foo parametresinin türü. bar_t * aracılığıyla, bar_t foo_exported.h dışa aktarılır.

bar_t , daha fazla türün dışa aktarılmasıyla sonuçlanan, foo_exported.h aracılığıyla dışa aktarılan, foo_t türünde bir mfoo üyesi içerir:
  • int : m1 türüdür.
  • int * : m2 türüdür.
  • foo_private_t * : mPfoo .

Ancak foo_private_t, foo_private_t aracılığıyla dışa aktarılmadığından erişilebilir foo_exported.h . ( foot_private_t * opaktır, bu nedenle foo_private_t üzerinde yapılan değişikliklere izin verilir.)

Temel sınıf belirteçleri ve şablon parametreleri aracılığıyla ulaşılabilen türler için de benzer bir açıklama yapılabilir.

ABI uyumluluğunun sağlanması

Android.bp dosyalarında vendor_available: true ve vndk.enabled: true olarak işaretlenen kitaplıklar için ABI uyumluluğu sağlanmalıdır. Örneğin:

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

Dışa aktarılan bir işlev tarafından doğrudan veya dolaylı olarak erişilebilen veri türleri için, kitaplıkta yapılan aşağıdaki değişiklikler ABI'yi bozan olarak sınıflandırılır:

Veri tipi Tanım
Yapılar ve Sınıflar
  • Sınıf tipinin veya yapı tipinin boyutunu değiştirin.
  • Temel sınıflar
    • Temel sınıfları ekleyin veya kaldırın.
    • Neredeyse devralınan temel sınıfları ekleyin veya kaldırın.
    • Temel sınıfların sırasını değiştirin.
  • Üye işlevleri
    • Üye işlevlerini kaldırın*.
    • Üye işlevlerinden bağımsız değişkenler ekleyin veya kaldırın.
    • Bağımsız değişken türlerini veya üye işlevlerinin dönüş türlerini değiştirin*.
    • Sanal tablo düzenini değiştirin.
  • Veri üyeleri
    • Statik veri üyelerini kaldırın.
    • Statik olmayan veri üyeleri ekleyin veya kaldırın.
    • Veri üyelerinin türlerini değiştirin.
    • Ofsetleri statik olmayan veri üyeleriyle değiştirin**.
    • Veri üyelerinin const , volatile ve/veya restricted niteleyicilerini değiştirin***.
    • Veri üyelerinin erişim belirteçlerini düşürün***.
  • Şablon argümanlarını değiştirin.
sendikalar
  • Veri üyeleri ekleyin veya kaldırın.
  • Birleşim türünün boyutunu değiştirin.
  • Veri üyelerinin türlerini değiştirin.
  • Veri üyelerinin sırasını değiştirin.
numaralandırmalar
  • Temel alınan türü değiştirin.
  • Numaralandırıcıların adlarını değiştirin.
  • Numaralandırıcıların değerlerini değiştirin.
Küresel Semboller
  • Genel başlıklar tarafından dışa aktarılan sembolleri kaldırın.
  • FUNC türündeki global semboller için
    • Argüman ekleyin veya kaldırın.
    • Argüman türlerini değiştirin.
    • Dönüş türünü değiştirin.
    • Erişim belirtecini düşürün***.
  • OBJECT türündeki global semboller için
    • Karşılık gelen C/C++ türünü değiştirin.
    • Erişim belirtecini düşürün***.

* Hem genel hem de özel üye işlevleri değiştirilmemeli veya kaldırılmamalıdır, çünkü genel satır içi işlevler özel üye işlevlerine atıfta bulunabilir. Özel üye işlevlerine sembol referansları, arayan ikili dosyalarında tutulabilir. Özel üye işlevlerinin paylaşılan kitaplıklardan değiştirilmesi veya kaldırılması, geriye dönük uyumsuz ikili dosyalara neden olabilir.

** Genel veya özel veri üyelerine ofsetler değiştirilmemelidir çünkü satır içi işlevler, işlev gövdelerinde bu veri üyelerine başvurabilir. Veri üyesi ofsetlerinin değiştirilmesi, geriye dönük uyumsuz ikili dosyalara neden olabilir.

*** Bunlar türün bellek düzenini değiştirmese de, kitaplıkların beklendiği gibi çalışmamasına neden olabilecek anlamsal farklılıklar vardır.

ABI uyumluluk araçlarını kullanma

Bir VNDK kitaplığı oluşturulduğunda, kitaplığın ABI'si, oluşturulan VNDK sürümü için karşılık gelen ABI referansıyla karşılaştırılır. Referans ABI dökümleri şurada bulunur:

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/<${PLATFORM_VNDK_VERSION}>/<BINDER_BITNESS>/<ARCH_ARCH-VARIANT>/source-based

Örneğin, VNDK'nın 27. API düzeyi için libfoo oluştururken, libfoo çıkarsanan ABI'si şu adresteki referansıyla karşılaştırılır:

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/27/64/<ARCH_ARCH-VARIANT>/source-based/libfoo.so.lsdump

ABI kırılma hatası

ABI kesintilerinde, yapı günlüğü uyarı türü ve abi-diff raporunun yolu ile uyarıları görüntüler. Örneğin, libbinder uyumsuz bir değişiklik varsa, yapı sistemi aşağıdakine benzer bir mesajla bir hata verir:

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

VNDK kitaplığı oluşturma ABI kontrolleri

Bir VNDK kitaplığı oluşturulduğunda:

  1. header-abi-dumper , her kaynağa karşılık gelen .sdump dosyalarını üretmek için VNDK kitaplığını (kitaplığın kendi kaynak dosyalarının yanı sıra statik geçişli bağımlılıklar yoluyla devralınan kaynak dosyaları) oluşturmak için derlenen kaynak dosyalarını işler.
    sdump creation
    Şekil 1. .sdump dosyalarının oluşturulması
  2. header-abi-linker daha sonra, paylaşılan kitaplığa karşılık gelen tüm ABI bilgilerini günlüğe kaydeden bir .lsdump dosyası üretmek için .sdump dosyalarını (kendisine sağlanan bir sürüm komut dosyasını veya paylaşılan kitaplığa karşılık gelen .so dosyasını kullanarak) işler.
    lsdump creation
    Şekil 2. .lsdump dosyasının oluşturulması
  3. header-abi-diff , iki kitaplığın ABI'lerindeki farklılıkları özetleyen bir fark raporu oluşturmak için .lsdump dosyasını bir referans .lsdump dosyasıyla karşılaştırır.
    abi diff creation
    Şekil 3. Fark raporunun oluşturulması

başlık-abi-damper

header-abi-dumper aracı, bir C/C++ kaynak dosyasını ayrıştırır ve bu kaynak dosyadan çıkarılan ABI'yi bir ara dosyaya döker. Derleme sistemi, derlenmiş tüm kaynak dosyalar üzerinde header-abi-dumper çalıştırırken aynı zamanda geçişli bağımlılıklardan kaynak dosyaları içeren bir kitaplık oluşturur.

Şu anda .sdump dosyaları Protobuf TextFormatted olarak biçimlendirilmiştir ve bu, gelecekteki sürümlerde kararlı olacağı garanti edilmez. Bu nedenle, .sdump dosya biçimlendirmesi, bir yapı sistemi uygulama ayrıntısı olarak düşünülmelidir.

Örneğin, libfoo.so aşağıdaki kaynak dosyasına sahiptir 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;
}

Aşağıdakileri kullanarak kaynak dosya tarafından sunulan ABI'yi temsil eden bir ara .sdump dosyası oluşturmak için header-abi-dumper dumper'ı kullanabilirsiniz:

$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -x c++

Bu komut, header-abi-dumper foo.cpp ayrıştırmasını ve exported dizindeki genel başlıklarda gösterilen ABI bilgilerini yaymasını söyler. Bu, header-abi-dumper tarafından oluşturulan foo.sdump bir alıntıdır (tam bir temsil değildir):

record_types {
  type_info {
    name: "foo"
    size: 12
    alignment: 4
    referenced_type: "type-1"
    source_file: "foo/include/foo_exported.h"
    linker_set_key: "foo"
    self_type: "type-1"
  }
  fields {
    referenced_type: "type-2"
    field_offset: 0
    field_name: "m1"
    access: public_access
  }
  fields {
    referenced_type: "type-3"
    field_offset: 32
    field_name: "m2"
    access: public_access
  }
  fields {
    referenced_type: "type-5"
    field_offset: 64
    field_name: "mPfoo"
    access: public_access
  }
  access: public_access
  record_kind: struct_kind
  tag_info {
    unique_id: "_ZTS3foo"
  }
}
record_types {
  type_info {
    name: "bar"
    size: 12
    alignment: 4
    referenced_type: "type-6"
…
pointer_types {
  type_info {
    name: "bar *"
    size: 4
    alignment: 4
    referenced_type: "type-6"
    source_file: "foo/include/foo_exported.h"
    linker_set_key: "bar *"
    self_type: "type-8"
  }
}
builtin_types {
  type_info {
    name: "int"
    size: 4
    alignment: 4
    referenced_type: "type-2"
    source_file: ""
    linker_set_key: "int"
    self_type: "type-2"
  }
  is_unsigned: false
  is_integral: true
}
functions {
  return_type: "type-7"
  function_name: "Foo"
  source_file: "foo/include/foo_exported.h"
  parameters {
    referenced_type: "type-2"
    default_arg: false
  }
  parameters {
    referenced_type: "type-8"
    default_arg: false
  }
  linker_set_key: "_Z3FooiP3bar"
  access: public_access
}

foo.sdump , foo.cpp kaynak dosyası tarafından sunulan ABI bilgilerini içerir, örneğin:

  • record_types . Genel başlıklar tarafından açığa çıkarılan yapılara, birleşimlere veya sınıflara bakın. Her kayıt türü, alanları, boyutu, erişim belirteci, maruz kaldığı başlık dosyası vb. hakkında bilgilere sahiptir.
  • pointer_types . Genel başlıklar tarafından gösterilen kayıtlar/işlevler tarafından doğrudan/dolaylı olarak başvurulan işaretçi türlerine ve işaretçinin işaret ettiği türe bakın ( type_info içindeki referenced_type alanı aracılığıyla). Nitelikli türler, yerleşik C/C++ türleri, dizi türleri ve değer ve değer referans türleri için .sdump dosyasında benzer bilgiler günlüğe kaydedilir (türlerle ilgili bu tür günlük kaydı bilgileri, özyinelemeli farklılaşmaya izin verir).
  • functions . Genel üstbilgiler tarafından sunulan işlevleri temsil eder. Ayrıca işlevin karışık adı, dönüş türü, parametre türleri, erişim belirteci vb. hakkında da bilgileri vardır.

başlık-abi-bağlayıcı

header-abi-linker aracı, header-abi-dumper tarafından üretilen ara dosyaları girdi olarak alır ve ardından bu dosyaları birbirine bağlar:

girişler
  • header-abi-dumper tarafından üretilen ara dosyalar
  • Sürüm komut dosyası/Harita dosyası (isteğe bağlı)
  • .so paylaşılan kitaplığın dosyası
Çıktı Paylaşılan bir kitaplığın ABI'sini günlüğe kaydeden bir dosya (örn. libfoo.so.lsdump , libfoo ABI'sini temsil eder).

Araç, kendisine verilen tüm ara dosyalardaki tür grafiklerini, çeviri birimleri arasındaki tek tanımlı (farklı çeviri birimlerinde aynı tam ada sahip kullanıcı tanımlı türler, anlamsal olarak farklı olabilir) farklılıkları dikkate alarak birleştirir. Araç daha sonra dışa aktarılan sembollerin bir listesini yapmak için paylaşılan kitaplığın ( .so dosyası) bir sürüm komut dosyasını veya .dynsym tablosunu ayrıştırır.

Örneğin, libfoo , bar.cpp dosyasını (bir C işlev bar gösterir) eklediğinde, libfoo tam bağlantılı ABI dökümünü oluşturmak için header-abi-linker aşağıdaki gibi çağrılabilir:

header-abi-linker -I exported foo.sdump bar.sdump \
                  -o libfoo.so.lsdump \
                  -so libfoo.so \
                  -arch arm64 -api current

libfoo.so.lsdump içindeki örnek komut çıktısı:

record_types {
  type_info {
    name: "foo"
    size: 24
    alignment: 8
    referenced_type: "type-1"
    source_file: "foo/include/foo_exported.h"
    linker_set_key: "foo"
    self_type: "type-1"
  }
  fields {
    referenced_type: "type-2"
    field_offset: 0
    field_name: "m1"
    access: public_access
  }
  fields {
    referenced_type: "type-3"
    field_offset: 64
    field_name: "m2"
    access: public_access
  }
  fields {
    referenced_type: "type-4"
    field_offset: 128
    field_name: "mPfoo"
    access: public_access
  }
  access: public_access
  record_kind: struct_kind
  tag_info {
    unique_id: "_ZTS3foo"
  }
}
record_types {
  type_info {
    name: "bar"
    size: 24
    alignment: 8
...
builtin_types {
  type_info {
    name: "void"
    size: 0
    alignment: 0
    referenced_type: "type-6"
    source_file: ""
    linker_set_key: "void"
    self_type: "type-6"
  }
  is_unsigned: false
  is_integral: false
}
functions {
  return_type: "type-19"
  function_name: "Foo"
  source_file: "foo/include/foo_exported.h"
  parameters {
    referenced_type: "type-2"
    default_arg: false
  }
  parameters {
    referenced_type: "type-20"
    default_arg: false
  }
  linker_set_key: "_Z3FooiP3bar"
  access: public_access
}
functions {
  return_type: "type-6"
  function_name: "FooBad"
  source_file: "foo/include/foo_exported_bad.h"
  parameters {
    referenced_type: "type-2"
    default_arg: false
  }
parameters {
    referenced_type: "type-7"
    default_arg: false
  }
  linker_set_key: "_Z6FooBadiP3foo"
  access: public_access
}
elf_functions {
  name: "_Z3FooiP3bar"
}
elf_functions {
  name: "_Z6FooBadiP3foo"
}

header-abi-linker aracı:

  • Kendisine sağlanan .sdump dosyalarını ( foo.sdump ve bar.sdump ) bağlar, dizinde bulunan başlıklarda bulunmayan ABI bilgilerini filtreler: exported aktarılır.
  • libfoo.so dosyasını ayrıştırır ve .dynsym tablosu aracılığıyla kitaplık tarafından dışa aktarılan semboller hakkında bilgi toplar.
  • _Z3FooiP3bar ve Bar ekler.

libfoo.so.lsdump , libfoo.so'nun son oluşturulan ABI libfoo.so .

başlık-abi-diff

header-abi-diff aracı, iki kitaplığın ABI'sini temsil eden iki .lsdump dosyasını karşılaştırır ve iki ABI arasındaki farkları belirten bir fark raporu üretir.

girişler
  • .lsdump dosyası, eski bir paylaşılan kitaplığın ABI'sini temsil eder.
  • Yeni bir paylaşılan kitaplığın ABI'sini temsil eden .lsdump dosyası.
Çıktı Karşılaştırılan iki paylaşılan kitaplık tarafından sunulan ABI'lerdeki farklılıkları belirten farklı bir rapor.

ABI diff dosyası, olabildiğince ayrıntılı ve okunabilir olacak şekilde tasarlanmıştır. Biçim, gelecekteki sürümlerde değişebilir. Örneğin, libfoo_old.so libfoo libfoo_new.so . libfoo_new.so içinde, bar_t içinde, mfoo türünü mfoo yerine foo_t foo_t * olarak değiştirirsiniz. bar_t doğrudan erişilebilir bir tür olduğundan, bu, header-abi-diff tarafından bir ABI kırılma değişikliği olarak işaretlenmelidir.

header-abi-diff çalıştırmak için:

header-abi-diff -old libfoo_old.so.lsdump \
                -new libfoo_new.so.lsdump \
                -arch arm64 \
                -o libfoo.so.abidiff \
                -lib libfoo

libfoo.so.abidiff içindeki örnek komut çıktısı:

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 tüm ABI kırılma değişikliklerinin bir raporunu içerir. record_type_diffs mesajı, bir kaydın değiştiğini belirtir ve aşağıdakileri içeren uyumsuz değişiklikleri listeler:

  • 24 bayttan 8 bayta değişen kaydın boyutu.
  • mfoo'nun alan türü mfoo foo foo * 'ya değişir (tüm typedef'ler çıkarılır).

type_stack alanı, header-abi-diff değişen türe ( bar ) nasıl ulaştığını gösterir. Bu alan, Foo parametre olarak bar * alan, dışa aktarılan ve değiştirilen bar öğesine işaret eden dışa aktarılan bir işlev olduğu şeklinde yorumlanabilir.

ABI/API'yi zorlama

VNDK ve LLNDK paylaşılan kitaplıklarının ABI/API'sini uygulamak için ABI referansları ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/ / içinde kontrol edilmelidir. Bu referansları oluşturmak için aşağıdaki komutu çalıştırın:

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

Referansları oluşturduktan sonra, VNDK veya LLNDK kitaplığında uyumsuz bir ABI/API değişikliğiyle sonuçlanan kaynak kodunda yapılan herhangi bir değişiklik artık bir derleme hatasıyla sonuçlanıyor.

Belirli VNDK çekirdek kitaplıkları için ABI referanslarını güncellemek için aşağıdaki komutu çalıştırın:

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

Örneğin, libbinder ABI referanslarını güncellemek için şunu çalıştırın:

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

Belirli LLNDK kitaplıkları için ABI referanslarını güncellemek için aşağıdaki komutu çalıştırın:

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

Örneğin, libm ABI referanslarını güncellemek için şunu çalıştırın:

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