Saya tidak pernah mengerti dengan jelas apa itu ABI. Tolong jangan arahkan saya ke artikel Wikipedia. Jika saya bisa memahaminya, saya tidak akan berada di sini memposting posting yang panjang.
Ini adalah pola pikir saya tentang antarmuka yang berbeda:
Remote TV adalah antarmuka antara pengguna dan TV. Ini adalah entitas yang sudah ada, tetapi tidak berguna (tidak menyediakan fungsi apa pun) dengan sendirinya. Semua fungsi untuk masing-masing tombol pada kendali jarak jauh diimplementasikan di perangkat televisi.
Antarmuka: Ini adalah lapisan "entitas yang ada" antara
functionality
danconsumer
fungsionalitas itu. Antarmuka dengan sendirinya tidak melakukan apa pun. Itu hanya memanggil fungsi yang ada di belakang.Sekarang tergantung pada siapa pengguna ada berbagai jenis antarmuka.
Command Line Interface (CLI) perintah adalah entitas yang ada, konsumen adalah pengguna dan fungsi di belakang.
functionality:
fungsi perangkat lunak saya yang memecahkan beberapa tujuan yang kami uraikan antarmuka ini.
existing entities:
perintah
consumer:
penggunaJendela Antarmuka Pengguna Grafis (GUI) , tombol, dll. Adalah entitas yang ada, dan sekali lagi konsumen adalah pengguna dan fungsionalitas ada di belakang.
functionality:
fungsionalitas perangkat lunak saya yang memecahkan beberapa masalah yang kami uraikan antarmuka ini.
existing entities:
jendela, tombol dll.
consumer:
penggunaFungsi antarmuka Pemrograman Aplikasi (API) (atau lebih tepatnya) antarmuka (dalam pemrograman berbasis antarmuka) adalah entitas yang ada, konsumen di sini adalah program lain bukan pengguna, dan lagi fungsionalitas terletak di belakang lapisan ini.
functionality:
fungsionalitas perangkat lunak saya yang memecahkan beberapa masalah yang kami uraikan antarmuka ini.
existing entities:
fungsi, Antarmuka (array fungsi).
consumer:
program / aplikasi lain.Application Binary Interface (ABI) Di sinilah masalah saya mulai.
functionality:
???
existing entities:
???
consumer:
???
- Saya telah menulis perangkat lunak dalam berbagai bahasa dan menyediakan berbagai jenis antarmuka (CLI, GUI, dan API), tetapi saya tidak yakin apakah saya pernah menyediakan ABI.
ABI mencakup rincian seperti
- tipe data, ukuran, dan perataan;
- konvensi pemanggilan, yang mengontrol bagaimana argumen fungsi dilewatkan dan mengembalikan nilai yang diambil;
- nomor panggilan sistem dan bagaimana aplikasi harus membuat panggilan sistem ke sistem operasi;
Detail standar ABI lainnya seperti
- mangling nama C ++,
- propagasi pengecualian, dan
- konvensi pemanggilan antar kompiler pada platform yang sama, tetapi tidak memerlukan kompatibilitas lintas platform.
Siapa yang butuh detail ini? Tolong jangan katakan OS. Saya tahu pemrograman perakitan. Saya tahu cara menautkan & memuat berfungsi. Saya tahu persis apa yang terjadi di dalam.
Mengapa C ++ name mangling masuk? Saya pikir kita berbicara di tingkat biner. Mengapa bahasa bisa digunakan?
Lagi pula, saya sudah mengunduh [PDF] System V Application Binary Interface Edition 4.1 (1997-03-18) untuk melihat apa tepatnya isinya. Yah, sebagian besar tidak masuk akal.
Mengapa ini berisi dua bab (4 & 5) untuk menggambarkan format file ELF ? Bahkan, ini adalah dua bab penting dari spesifikasi itu. Bab-bab selanjutnya adalah "khusus prosesor". Bagaimanapun, saya pikir itu adalah topik yang sama sekali berbeda. Tolong jangan katakan bahwa spesifikasi format file ELF adalah ABI. Itu tidak memenuhi syarat untuk menjadi antarmuka sesuai dengan definisi.
Saya tahu, karena kita berbicara pada tingkat rendah seperti itu pasti sangat spesifik. Tapi saya tidak yakin bagaimana itu "instruksi set arsitektur (ISA)" spesifik?
Di mana saya dapat menemukan ABI Microsoft Windows?
Jadi, ini adalah pertanyaan utama yang menggangguku.
Jawaban:
Salah satu cara mudah untuk memahami "ABI" adalah membandingkannya dengan "API".
Anda sudah terbiasa dengan konsep API. Jika Anda ingin menggunakan fitur, katakanlah, beberapa perpustakaan atau OS Anda, Anda akan memprogram melawan API. API terdiri dari tipe data / struktur, konstanta, fungsi, dll yang dapat Anda gunakan dalam kode Anda untuk mengakses fungsionalitas komponen eksternal itu.
ABI sangat mirip. Anggap saja sebagai versi kompilasi dari API (atau sebagai API pada tingkat bahasa mesin). Saat Anda menulis kode sumber, Anda mengakses perpustakaan melalui API. Setelah kode dikompilasi, aplikasi Anda mengakses data biner di perpustakaan melalui ABI. ABI mendefinisikan struktur dan metode yang akan digunakan aplikasi terkompilasi Anda untuk mengakses perpustakaan eksternal (seperti halnya API), hanya pada tingkat yang lebih rendah. API Anda menentukan urutan di mana Anda meneruskan argumen ke suatu fungsi. ABI Anda menentukan mekanisme bagaimanaargumen ini diteruskan (register, stack, dll.). API Anda menentukan fungsi mana yang merupakan bagian dari perpustakaan Anda. ABI Anda menentukan bagaimana kode Anda disimpan di dalam file perpustakaan, sehingga program apa pun yang menggunakan perpustakaan Anda dapat menemukan fungsi yang diinginkan dan menjalankannya.
ABI sangat penting dalam hal aplikasi yang menggunakan perpustakaan eksternal. Perpustakaan penuh dengan kode dan sumber daya lainnya, tetapi program Anda harus tahu cara menemukan apa yang dibutuhkan di dalam file perpustakaan. ABI Anda menentukan bagaimana konten perpustakaan disimpan di dalam file, dan program Anda menggunakan ABI untuk mencari melalui file dan menemukan apa yang dibutuhkan. Jika semua yang ada di sistem Anda sesuai dengan ABI yang sama, maka setiap program dapat bekerja dengan file perpustakaan apa pun, tidak peduli siapa yang membuatnya. Linux dan Windows menggunakan ABI yang berbeda, sehingga program Windows tidak akan tahu cara mengakses perpustakaan yang dikompilasi untuk Linux.
Terkadang, perubahan ABI tidak dapat dihindari. Ketika ini terjadi, semua program yang menggunakan pustaka itu tidak akan berfungsi kecuali jika mereka dikompilasi ulang untuk menggunakan pustaka versi baru. Jika ABI berubah tetapi API tidak, maka versi pustaka lama dan baru kadang-kadang disebut "sumber kompatibel". Ini menyiratkan bahwa sementara program yang dikompilasi untuk satu versi pustaka tidak akan bekerja dengan yang lain, kode sumber yang ditulis untuk satu akan bekerja untuk yang lain jika dikompilasi ulang.
Karena alasan ini, pengembang cenderung mencoba menjaga ABI mereka stabil (untuk meminimalkan gangguan). Menjaga ABI stabil berarti tidak mengubah antarmuka fungsi (tipe dan jumlah pengembalian, jenis, dan urutan argumen), definisi tipe data atau struktur data, konstanta yang ditentukan, dll. Fungsi dan tipe data baru dapat ditambahkan, tetapi yang sudah ada harus tetap, sama. Jika, misalnya, perpustakaan Anda menggunakan bilangan bulat 32-bit untuk menunjukkan offset fungsi dan Anda beralih ke bilangan bulat 64-bit, maka kode yang sudah dikompilasi yang menggunakan perpustakaan itu tidak akan mengakses bidang itu (atau yang mengikutinya) dengan benar . Mengakses anggota struktur data akan dikonversi menjadi alamat dan offset memori selama kompilasi dan jika struktur data berubah,
ABI tidak harus sesuatu yang Anda akan berikan secara eksplisit kecuali Anda melakukan pekerjaan desain sistem tingkat sangat rendah. Ini juga tidak khusus untuk bahasa tertentu, karena (misalnya) aplikasi C dan aplikasi Pascal dapat menggunakan ABI yang sama setelah dikompilasi.
Edit:Mengenai pertanyaan Anda tentang bab-bab tentang format file ELF dalam dokumen SysV ABI: Alasan mengapa informasi ini dimasukkan adalah karena format ELF menentukan antarmuka antara sistem operasi dan aplikasi. Saat Anda memberi tahu OS untuk menjalankan suatu program, ia mengharapkan program diformat dengan cara tertentu dan (misalnya) mengharapkan bagian pertama dari biner untuk menjadi header ELF yang berisi informasi tertentu pada offset memori tertentu. Ini adalah bagaimana aplikasi mengkomunikasikan informasi penting tentang dirinya ke sistem operasi. Jika Anda membangun sebuah program dalam format biner non-ELF (seperti a.out atau PE), maka OS yang mengharapkan aplikasi yang diformat ELF tidak akan dapat menafsirkan file biner atau menjalankan aplikasi.
IIRC, Windows saat ini menggunakan format Portable Executable (atau, PE). Ada tautan di bagian "tautan eksternal" halaman Wikipedia dengan informasi lebih lanjut tentang format PE.
Juga, mengenai catatan Anda tentang susunan nama C ++: Saat menemukan fungsi dalam file perpustakaan, fungsi tersebut biasanya dicari berdasarkan namanya. C ++ memungkinkan Anda untuk membebani nama fungsi, jadi nama saja tidak cukup untuk mengidentifikasi suatu fungsi. Kompiler C ++ memiliki cara mereka sendiri untuk berurusan dengan ini secara internal, yang disebut nama mangling . ABI dapat menentukan cara pengkodean nama fungsi standar sehingga program yang dibangun dengan bahasa atau kompiler yang berbeda dapat menemukan apa yang mereka butuhkan. Saat Anda menggunakan
extern "c"
dalam program C ++, Anda menginstruksikan kompiler untuk menggunakan cara standar untuk merekam nama yang dapat dimengerti oleh perangkat lunak lain.sumber
Jika Anda mengetahui perakitan dan cara kerja di level OS, Anda menyesuaikan diri dengan ABI tertentu. ABI mengatur hal-hal seperti bagaimana parameter dilewatkan, di mana nilai kembali ditempatkan. Untuk banyak platform hanya ada satu ABI untuk dipilih, dan dalam kasus-kasus ABI hanya "cara kerja".
Namun, ABI juga mengatur hal-hal seperti bagaimana kelas / objek diletakkan dalam C ++. Ini diperlukan jika Anda ingin dapat melewatkan referensi objek melintasi batas-batas modul atau jika Anda ingin mencampur kode yang dikompilasi dengan kompiler yang berbeda.
Juga, jika Anda memiliki OS 64-bit yang dapat menjalankan binari 32-bit, Anda akan memiliki ABI yang berbeda untuk kode 32-dan 64-bit.
Secara umum, kode apa pun yang Anda tautkan ke executable yang sama harus sesuai dengan ABI yang sama. Jika Anda ingin berkomunikasi antara kode menggunakan ABI yang berbeda, Anda harus menggunakan beberapa bentuk RPC atau protokol serialisasi.
Saya pikir Anda berusaha terlalu keras untuk memeras berbagai jenis antarmuka menjadi serangkaian karakteristik yang tetap. Sebagai contoh, sebuah antarmuka tidak harus dibagi menjadi konsumen dan produsen. Antarmuka hanyalah konvensi tempat dua entitas berinteraksi.
ABI dapat (sebagian) ISA-agnostik. Beberapa aspek (seperti konvensi pemanggilan) bergantung pada ISA, sementara aspek lainnya (seperti tata letak kelas C ++) tidak.
ABI yang terdefinisi dengan baik sangat penting bagi orang yang menulis kompiler. Tanpa ABI yang terdefinisi dengan baik, tidak mungkin menghasilkan kode yang dapat dioperasikan.
EDIT: Beberapa catatan untuk menjelaskan:
sumber
Anda sebenarnya tidak membutuhkan ABI sama sekali if--
Ringkasan yang terlalu disederhanakan:
ABI adalah seperangkat aturan yang dipatuhi oleh kompiler dan penghubung untuk mengkompilasi program Anda sehingga akan berfungsi dengan baik. ABI mencakup banyak topik:
Melihat lebih dalam pada konvensi pemanggilan, yang saya anggap sebagai inti dari ABI:
Mesin itu sendiri tidak memiliki konsep "fungsi". Ketika Anda menulis fungsi dalam bahasa tingkat tinggi seperti c, kompiler menghasilkan garis kode perakitan seperti
_MyFunction1:
. Ini adalah label , yang pada akhirnya akan diselesaikan menjadi alamat oleh assembler. Label ini menandai "awal" dari "fungsi" Anda dalam kode rakitan. Dalam kode tingkat tinggi, ketika Anda "memanggil" fungsi itu, apa yang sebenarnya Anda lakukan menyebabkan CPU beralih ke alamat label itu dan melanjutkan eksekusi di sana.Dalam persiapan untuk lompat, kompiler harus melakukan banyak hal penting. Konvensi panggilan seperti daftar periksa yang diikuti oleh kompiler untuk melakukan semua hal ini:
_MyFunction1:
). Pada titik ini, Anda dapat menganggap CPU sebagai "dalam" fungsi "Anda.Ada banyak ABI / konvensi panggilan yang berbeda. Beberapa yang utama adalah:
Ini adalah halaman yang bagus yang benar-benar menunjukkan perbedaan dalam perakitan yang dihasilkan saat mengkompilasi ABI yang berbeda.
Hal lain yang perlu diperhatikan adalah bahwa ABI tidak hanya relevan di dalam modul program Anda yang dapat dieksekusi. Ini juga digunakan oleh tautan untuk memastikan program Anda memanggil fungsi perpustakaan dengan benar. Anda memiliki beberapa pustaka bersama yang berjalan di komputer Anda, dan selama kompiler Anda tahu ABI apa yang mereka gunakan masing-masing, ia dapat memanggil fungsi dari mereka dengan benar tanpa meledakkan tumpukan.
Kompiler Anda memahami cara memanggil fungsi pustaka sangat penting. Pada platform yang dihosting (yaitu, di mana OS memuat program), program Anda bahkan tidak bisa berkedip tanpa melakukan panggilan kernel.
sumber
Antarmuka biner aplikasi (ABI) mirip dengan API, tetapi fungsinya tidak dapat diakses oleh pemanggil di tingkat kode sumber. Hanya representasi biner yang dapat diakses / tersedia.
ABI dapat didefinisikan pada tingkat arsitektur prosesor atau pada tingkat OS. ABI adalah standar yang harus diikuti oleh fase pembuat kode dari kompiler. Standar ditetapkan baik oleh OS atau oleh prosesor.
Fungsi: Menentukan mekanisme / standar untuk membuat panggilan fungsi terlepas dari bahasa implementasi atau kompiler / penghubung / toolchain tertentu. Berikan mekanisme yang memungkinkan JNI, atau antarmuka Python-C, dll.
Entitas yang ada: Fungsi dalam bentuk kode mesin.
Konsumen: Fungsi lain (termasuk satu dalam bahasa lain, dikompilasi oleh kompiler lain, atau ditautkan oleh tautan lain).
sumber
Fungsionalitas: Seperangkat kontrak yang memengaruhi kompiler, penulis majelis, penghubung, dan sistem operasi. Kontrak menentukan bagaimana fungsi diletakkan, di mana parameter dilewatkan, bagaimana parameter dilewatkan, bagaimana fungsi kembali bekerja. Ini umumnya khusus untuk tuple (arsitektur prosesor, sistem operasi).
Entitas yang ada: tata letak parameter, semantik fungsi, alokasi register. Sebagai contoh, arsitektur ARM memiliki banyak ABI (APCS, EABI, GNU-EABI, tidak peduli banyak kasus sejarah) - menggunakan ABI campuran akan menghasilkan kode Anda hanya tidak berfungsi ketika memanggil melintasi batas.
Konsumen: Kompiler, penulis perakitan, sistem operasi, arsitektur khusus CPU.
Siapa yang butuh detail ini? Kompiler, penulis perakitan, penghubung yang melakukan pembuatan kode (atau persyaratan pelurusan), sistem operasi (penanganan interupsi, antarmuka syscall). Jika Anda melakukan pemrograman perakitan, Anda menyesuaikan diri dengan ABI!
C ++ name mangling adalah kasus khusus - itu adalah masalah linker dan penghubung dinamis yang berpusat - jika nama mangling tidak dibakukan, maka penautan dinamis tidak akan berfungsi. Selanjutnya, ABI C ++ disebut hanya itu, ABI C ++. Ini bukan masalah level tautan, melainkan masalah pembuatan kode. Setelah Anda memiliki biner C ++, tidak mungkin membuatnya kompatibel dengan C ++ ABI lain (nama mangling, penanganan perkecualian) tanpa mengkompilasi ulang dari sumber.
ELF adalah format file untuk penggunaan loader dan tautan dinamis. ELF adalah format wadah untuk kode biner dan data, dan dengan demikian menentukan ABI dari sepotong kode. Saya tidak akan menganggap ELF sebagai ABI dalam arti yang ketat, karena PE executable bukan ABI.
Semua ABI adalah set instruksi khusus. ABM ARM tidak akan masuk akal pada prosesor MSP430 atau x86_64.
Windows memiliki beberapa ABI - misalnya, fastcall dan stdcall adalah dua ABI yang umum digunakan. ABI syscall berbeda lagi.
sumber
Biarkan saya setidaknya menjawab sebagian dari pertanyaan Anda. Dengan sebuah contoh tentang bagaimana ABI Linux memengaruhi panggilan sistem, dan mengapa itu berguna.
Panggilan sistem adalah cara bagi program userspace untuk menanyakan sesuatu kepada kernel. Ini bekerja dengan meletakkan kode numerik untuk panggilan dan argumen dalam register tertentu dan memicu interupsi. Daripada terjadi pergantian ke kernel dan kernel mencari kode numerik dan argumen, menangani permintaan, menempatkan hasilnya kembali ke register dan memicu peralihan kembali ke ruang pengguna. Ini diperlukan misalnya ketika aplikasi ingin mengalokasikan memori atau membuka file (syscalls "brk" dan "open").
Sekarang syscalls memiliki nama pendek "brk", dll. Dan opcodes yang sesuai, ini didefinisikan dalam file header spesifik sistem. Selama opcode ini tetap sama, Anda dapat menjalankan program userland yang dikompilasi sama dengan kernel yang diperbarui tanpa harus mengkompilasi ulang. Jadi Anda memiliki antarmuka yang digunakan oleh biner yang dikompilasi, karenanya ABI.
sumber
Untuk memanggil kode di pustaka bersama, atau kode panggilan antara unit kompilasi, file objek harus berisi label untuk panggilan. C ++ menguraikan nama-nama label metode untuk menegakkan penyembunyian data dan memungkinkan metode kelebihan beban. Itulah sebabnya Anda tidak dapat mencampur file dari berbagai kompiler C ++ kecuali mereka secara eksplisit mendukung ABI yang sama.
sumber
Cara terbaik untuk membedakan antara ABI dan API adalah untuk mengetahui mengapa dan untuk apa ia digunakan:
Untuk x86-64 umumnya ada satu ABI (dan untuk x86 32-bit ada set lain):
http://www.x86-64.org/documentation/abi.pdf
https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/140-x86-64_Function_Calling_Conventions/x86_64.html
http://people.freebsd.org/~obrien/amd64-elf-abi.pdf
Linux + FreeBSD + MacOSX mengikutinya dengan beberapa variasi. Dan Windows x64 memiliki ABI sendiri:
http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
Mengetahui ABI dan mengasumsikan kompiler lain mengikutinya juga, maka binari secara teoritis tahu bagaimana memanggil satu sama lain (perpustakaan khususnya API) dan melewatkan parameter di atas tumpukan atau dengan register dll. Atau register apa yang akan diubah saat memanggil fungsi dll Pada dasarnya pengetahuan ini akan membantu perangkat lunak untuk berintegrasi satu sama lain. Mengetahui urutan tata letak register / stack saya dapat dengan mudah menyatukan berbagai perangkat lunak yang ditulis dalam rakitan bersama tanpa banyak masalah.
Tetapi API berbeda:
Ini adalah nama fungsi tingkat tinggi, dengan argumen yang ditentukan, sehingga jika bagian-bagian perangkat lunak yang berbeda menggunakan API ini, MUNGKIN dapat saling memanggil satu sama lain. Tetapi persyaratan tambahan dari ABI SAMA harus dipatuhi.
Misalnya, Windows dulunya kompatibel dengan POSIX API:
https://en.wikipedia.org/wiki/Windows_Services_for_UNIX
https://en.wikipedia.org/wiki/POSIX
Dan Linux juga sesuai dengan POSIX. Tetapi binari tidak bisa hanya dipindahkan dan dijalankan segera. Tetapi karena mereka menggunakan NAMES yang sama dalam API yang sesuai dengan POSIX, Anda dapat mengambil perangkat lunak yang sama dalam C, mengkompilasi ulangnya di OS yang berbeda, dan segera menjalankannya.
API dimaksudkan untuk memudahkan integrasi perangkat lunak - tahap pra-kompilasi. Jadi setelah kompilasi, perangkat lunak dapat terlihat sangat berbeda - jika ABI berbeda.
ABI dimaksudkan untuk menentukan integrasi perangkat lunak yang tepat pada tingkat biner / perakitan.
sumber
SYS_execve
adalah 11 pada linux 32bit, tetapi 59 pada FreeBSD.personality(2)
dapat diaturPER_BSD
. Saya pikir saya ingat melihatpersonality(PER_LINUX)
distrace
keluaran sepanjang waktu, tetapi 64bit yang modern binari Linux tidak melakukan itu lagi.Linux shared library contoh ABI minimal runnable
Dalam konteks perpustakaan bersama, implikasi paling penting dari "memiliki ABI stabil" adalah bahwa Anda tidak perlu mengkompilasi ulang program Anda setelah perpustakaan berubah.
Jadi misalnya:
jika Anda menjual perpustakaan bersama, Anda menyimpan pengguna Anda gangguan mengkompilasi ulang segala sesuatu yang tergantung pada perpustakaan Anda untuk setiap rilis baru
jika Anda menjual program sumber tertutup yang bergantung pada pustaka bersama yang ada dalam distribusi pengguna, Anda bisa merilis dan menguji lebih sedikit prebuilt jika Anda yakin ABI stabil di seluruh versi tertentu dari OS target.
Ini khususnya penting dalam kasus pustaka standar C, yang banyak tautannya dengan banyak program di sistem Anda.
Sekarang saya ingin memberikan contoh runnable beton minimal ini.
main.c
mylib.c
mylib.h
Kompilasi dan berjalan dengan baik dengan:
Sekarang, misalkan untuk v2 perpustakaan, kami ingin menambahkan bidang baru untuk
mylib_mystruct
dipanggilnew_field
.Jika kami menambahkan bidang sebelumnya
old_field
seperti pada:dan membangun kembali perpustakaan tetapi tidak
main.out
, maka pernyataan itu gagal!Ini karena baris:
telah menghasilkan perakitan yang mencoba mengakses yang pertama
int
dari struct, yang sekarangnew_field
bukan yang diharapkanold_field
.Karenanya perubahan ini mematahkan ABI.
Namun, jika kami tambahkan
new_field
setelahold_field
:kemudian perakitan lama yang dihasilkan masih mengakses yang pertama
int
dari struct, dan program masih bekerja, karena kami menjaga ABI stabil.Ini adalah versi yang sepenuhnya otomatis dari contoh ini di GitHub .
Cara lain untuk menjaga ABI ini stabil adalah dengan memperlakukannya
mylib_mystruct
sebagai struct buram , dan hanya mengakses ladangnya melalui metode helpers. Ini membuatnya lebih mudah untuk menjaga ABI stabil, tetapi akan menimbulkan overhead kinerja karena kami akan melakukan lebih banyak panggilan fungsi.API vs ABI
Dalam contoh sebelumnya, menarik untuk dicatat bahwa menambahkan
new_field
sebelumnyaold_field
, hanya merusak ABI, tetapi bukan API.Apa artinya ini, adalah bahwa jika kita telah mengkompilasi ulang
main.c
program kita melawan perpustakaan, itu akan berhasil.Kami juga akan merusak API jika kami mengubah misalnya tanda tangan fungsi:
karena dalam kasus itu,
main.c
akan berhenti kompilasi sama sekali.API Semantik vs API Pemrograman
Kami juga dapat mengklasifikasikan perubahan API dalam tipe ketiga: perubahan semantik.
API semantik, biasanya merupakan deskripsi bahasa alami dari apa yang seharusnya dilakukan oleh API, biasanya termasuk dalam dokumentasi API.
Karena itu dimungkinkan untuk memecah API semantik tanpa merusak program itu sendiri.
Sebagai contoh, jika kita telah memodifikasi
untuk:
maka ini tidak akan merusak pemrograman API, maupun ABI, tetapi
main.c
API semantik akan rusak.Ada dua cara untuk memeriksa API kontrak secara terprogram:
verifikasi formal . Sulit dilakukan, tetapi menghasilkan bukti matematis tentang kebenaran, pada dasarnya menyatukan dokumentasi dan tes menjadi cara yang "dapat diverifikasi" oleh manusia / mesin! Selama tidak ada bug dalam deskripsi formal Anda tentu saja ;-)
Konsep ini terkait erat dengan formalisasi Matematika itu sendiri: /math/53969/what-does-formal-mean/3297537#3297537
Daftar semua yang merusak C / C ++ perpustakaan bersama ABI
TODO: temukan / buat daftar pamungkas:
Contoh minimal Java runnable
Apa kompatibilitas biner di Jawa?
Diuji di Ubuntu 18.10, GCC 8.2.0.
sumber
ABI harus konsisten antara penelepon dan callee untuk memastikan bahwa panggilan berhasil. Stack use, register use, end-of-rutin stack pop. Semua ini adalah bagian terpenting dari ABI.
sumber
Ringkasan
Ada berbagai interpretasi dan pendapat kuat dari layer yang tepat yang mendefinisikan ABI (aplikasi binary interface).
Dalam pandangan saya, ABI adalah konvensi subyektif dari apa yang dianggap sebagai platform / diberikan untuk API tertentu. ABI adalah "sisa" dari konvensi yang "tidak akan berubah" untuk API tertentu atau yang akan ditangani oleh lingkungan runtime: eksekutor, alat, penghubung, kompiler, jvm, dan OS.
Mendefinisikan Antarmuka : ABI, API
Jika Anda ingin menggunakan perpustakaan seperti joda-time, Anda harus mendeklarasikan ketergantungan
joda-time-<major>.<minor>.<patch>.jar
. Perpustakaan mengikuti praktik terbaik dan menggunakan Versi Semantik . Ini mendefinisikan kompatibilitas API pada tiga tingkatan:Agar Anda dapat menggunakan rilis utama baru dari perpustakaan yang sama, banyak konvensi lain yang masih harus dihormati:
Contohnya
Studi kasus Jawa
Sebagai contoh, Java menstandarkan semua konvensi ini, bukan dalam alat, tetapi dalam spesifikasi JVM formal. Spesifikasi memungkinkan vendor lain untuk menyediakan seperangkat alat yang berbeda yang dapat menampilkan pustaka yang kompatibel.
Java menyediakan dua studi kasus menarik lainnya untuk ABI: Versi Scala dan mesin virtual Dalvik .
Mesin virtual Dalvik memecahkan ABI
VM Dalvik membutuhkan jenis bytecode yang berbeda dari bytecode Java. Perpustakaan Dalvik diperoleh dengan mengkonversi bytecode Java (dengan API yang sama) untuk Dalvik. Dengan cara ini Anda bisa mendapatkan dua versi dari API yang sama: ditentukan oleh yang asli
joda-time-1.7.2.jar
. Kita bisa memanggilkujoda-time-1.7.2.jar
danjoda-time-1.7.2-dalvik.jar
. Mereka menggunakan ABI yang berbeda untuk Java vms standar yang berorientasi pada stack: Oracle, IBM, Java terbuka atau lainnya; dan ABI kedua adalah yang di sekitar Dalvik.Rilis berturut-turut Scala tidak kompatibel
Scala tidak memiliki kompatibilitas biner antara versi minor Scala: 2.X. Untuk alasan ini API yang sama "io.reactivex" %% "rxscala"% "0.26.5" memiliki tiga versi (di masa mendatang lebih banyak): untuk Scala 2.10, 2.11 dan 2.12. Apa yang diubah? Saya tidak tahu untuk saat ini , tetapi binari tidak kompatibel. Mungkin versi terbaru menambahkan hal-hal yang membuat perpustakaan tidak dapat digunakan di mesin virtual lama, mungkin hal-hal yang berkaitan dengan menghubungkan / memberi nama / konvensi parameter.
Rilis berturut-turut Java tidak kompatibel
Java juga memiliki masalah dengan rilis utama JVM: 4,5,6,7,8,9. Mereka hanya menawarkan kompatibilitas ke belakang. Jvm9 tahu bagaimana menjalankan kode yang dikompilasi / ditargetkan (
-target
opsi javac ) untuk semua versi lain, sementara JVM 4 tidak tahu bagaimana menjalankan kode yang ditargetkan untuk JVM 5. Semua ini sementara Anda memiliki satu joda-library. Ketidakcocokan ini terbang di bawah radar berkat solusi yang berbeda:Mengapa saya mulai dengan definisi API?
API dan ABI hanyalah konvensi tentang bagaimana Anda mendefinisikan kompatibilitas. Lapisan bawah bersifat umum sehubungan dengan semantik tingkat tinggi. Itu sebabnya mudah untuk membuat beberapa konvensi. Jenis konvensi pertama adalah tentang penyelarasan memori, pengkodean byte, konvensi pemanggilan, pengkodean endian besar dan kecil, dll. Di atasnya Anda mendapatkan konvensi yang dapat dieksekusi seperti yang dijelaskan, menghubungkan konvensi, kode byte menengah seperti yang digunakan oleh Java atau LLVM IR digunakan oleh GCC. Ketiga Anda mendapatkan konvensi tentang cara menemukan perpustakaan, cara memuatnya (lihat Java classloaders). Ketika Anda semakin tinggi dan semakin tinggi dalam konsep Anda memiliki konvensi baru yang Anda anggap sebagai yang diberikan. Itu sebabnya mereka tidak berhasil sampai ke versi semantik . Mereka tersirat atau runtuh dalamVersi: kapan. Kita dapat mengubah versi semantik dengan
<major>-<minor>-<patch>-<platform/ABI>
. Ini adalah apa yang sebenarnya terjadi sudah: Platform sudah merupakanrpm
,dll
,jar
(JVM bytecode),war
(JVM + web server),apk
,2.11
(khusus Scala versi) dan sebagainya. Ketika Anda mengatakan APK, Anda sudah membicarakan bagian ABI tertentu dari API Anda.API dapat porting ke ABI yang berbeda
Level teratas dari abstraksi (sumber yang ditulis dengan API tertinggi dapat dikompilasi ulang / porting ke abstraksi level bawah lainnya.
Katakanlah saya memiliki beberapa sumber untuk rxscala. Jika alat Scala diubah, saya dapat mengkompilasi ulang untuk itu. Jika perubahan JVM saya bisa memiliki konversi otomatis dari mesin lama ke yang baru tanpa mengganggu konsep tingkat tinggi. Sementara porting mungkin sulit akan membantu klien lain. Jika sistem operasi baru dibuat menggunakan kode assembler yang sama sekali berbeda, penerjemah dapat dibuat.
API porting lintas bahasa
Ada API yang porting dalam berbagai bahasa seperti aliran reaktif . Secara umum mereka mendefinisikan pemetaan untuk bahasa / platform tertentu. Saya berpendapat bahwa API adalah spesifikasi utama yang secara formal didefinisikan dalam bahasa manusia atau bahkan bahasa pemrograman tertentu. Semua "pemetaan" lainnya adalah ABI dalam arti tertentu, selain API lebih dari ABI biasa. Hal yang sama terjadi dengan antarmuka REST.
sumber
Singkatnya dan dalam filosofi, hanya hal-hal sejenis yang dapat berjalan dengan baik, dan ABI dapat dilihat sebagai jenis perangkat lunak yang bekerja bersama.
sumber
Saya juga berusaha memahami jawaban ABI dan JesperE yang sangat membantu.
Dari perspektif yang sangat sederhana, kita dapat mencoba memahami ABI dengan mempertimbangkan kompatibilitas biner.
KDE wiki mendefinisikan perpustakaan sebagai biner yang kompatibel "jika sebuah program yang terhubung secara dinamis ke versi perpustakaan sebelumnya terus berjalan dengan versi perpustakaan yang lebih baru tanpa perlu mengkompilasi ulang." Untuk lebih lanjut tentang tautan dinamis, lihat Menghubungkan statis vs menghubungkan dinamis
Sekarang, mari kita coba untuk melihat hanya aspek paling mendasar yang diperlukan untuk sebuah perpustakaan menjadi kompatibilitas biner (dengan asumsi tidak ada perubahan kode sumber ke perpustakaan):
Tentu, ada banyak detail lain tetapi ini sebagian besar juga mencakup ABI.
Lebih khusus untuk menjawab pertanyaan Anda, dari yang di atas, kami dapat menyimpulkan:
Semoga ini membantu!
sumber
Antarmuka biner aplikasi (ABI)
Kegunaan:
Entitas yang ada:
konsumen:
Ini diperlukan oleh siapa pun yang harus memastikan bahwa membangun rantai alat bekerja secara keseluruhan. Jika Anda menulis satu modul dalam bahasa assembly, yang lain dengan Python, dan bukannya boot-loader Anda sendiri ingin menggunakan sistem operasi, maka modul "aplikasi" Anda bekerja melintasi batas "biner" dan memerlukan persetujuan "antarmuka" seperti itu.
C ++ nama mangling karena file objek dari berbagai bahasa tingkat tinggi mungkin diperlukan untuk ditautkan dalam aplikasi Anda. Pertimbangkan untuk menggunakan pustaka standar GCC membuat panggilan sistem ke Windows yang dibangun dengan Visual C ++.
ELF adalah salah satu harapan yang mungkin dari tautan dari file objek untuk interpretasi, meskipun JVM mungkin memiliki beberapa ide lain.
Untuk aplikasi Windows RT Store, coba cari ARM ABI jika Anda benar-benar ingin membuat beberapa rantai alat bekerja bersama.
sumber
Istilah ABI digunakan untuk merujuk pada dua konsep yang berbeda tetapi terkait.
Ketika berbicara tentang kompiler itu merujuk pada aturan yang digunakan untuk menerjemahkan dari konstruksi level sumber ke konstruksi biner. Seberapa besar tipe datanya? bagaimana cara kerja tumpukan? bagaimana cara meneruskan parameter ke fungsi? register mana yang harus disimpan oleh penelepon vs callee?
Ketika berbicara tentang perpustakaan itu merujuk ke antarmuka biner yang disajikan oleh perpustakaan yang dikompilasi. Antarmuka ini adalah hasil dari sejumlah faktor termasuk kode sumber perpustakaan, aturan yang digunakan oleh kompiler dan dalam beberapa kasus definisi diambil dari perpustakaan lain.
Perubahan pada pustaka dapat mematahkan ABI tanpa merusak API. Pertimbangkan misalnya perpustakaan dengan antarmuka seperti.
dan pemrogram aplikasi menulis kode seperti
Pemrogram aplikasi tidak peduli tentang ukuran atau tata letak FOO, tetapi aplikasi biner berakhir dengan ukuran hard disk yang dikodekan. Jika pemrogram perpustakaan menambahkan bidang tambahan ke foo dan seseorang menggunakan perpustakaan biner baru dengan aplikasi biner lama maka perpustakaan dapat membuat dari batas akses memori.
OTOH jika pembuat perpustakaan telah mendesain API seperti mereka.
dan pemrogram aplikasi menulis kode seperti
Maka aplikasi biner tidak perlu tahu apa-apa tentang struktur FOO, yang semuanya bisa disembunyikan di dalam perpustakaan. Harga yang Anda bayar untuk itu adalah bahwa operasi tumpukan terlibat.
sumber
ABI
-Application Binary Interface
adalah tentang komunikasi kode mesin dalam runtime antara dua bagian program biner seperti - aplikasi, pustaka, OS ...ABI
menjelaskan bagaimana benda disimpan dalam memori dan bagaimana fungsinya disebut (calling convention
)Contoh API dan ABI yang bagus adalah ekosistem iOS dengan bahasa Swift .
Application
- Saat Anda membuat aplikasi menggunakan berbagai bahasa. Misalnya Anda dapat membuat aplikasi menggunakanSwift
danObjective-C
[Mixing Swift and Objective-C]Application - OS
- runtime -Swift runtime
danstandard libraries
merupakan bagian dari OS dan tidak boleh dimasukkan ke dalam setiap bundel (misalnya aplikasi, kerangka kerja). Ini sama seperti penggunaan Objective-CLibrary
-Module Stability
case - waktu kompilasi - Anda akan dapat mengimpor framework yang dibangun dengan versi lain dari kompiler Swift. Ini berarti aman untuk membuat biner sumber tertutup (pra-bangun) yang akan dikonsumsi oleh versi kompiler yang berbeda (.swiftinterface
digunakan bersama.swiftmodule
) dan Anda tidak akan mendapatkanLibrary
-Library Evolution
kasing[API vs ABI]
sumber