Nigdy nie zrozumiałem jasno, czym jest ABI. Proszę nie kierować mnie do artykułu w Wikipedii. Gdybym mógł to zrozumieć, nie byłoby mnie tutaj, publikującego tak długi post.
Oto moje zdanie na temat różnych interfejsów:
Pilot do telewizora to interfejs między użytkownikiem a telewizorem. Jest istniejącym bytem, ale sam w sobie jest bezużyteczny (nie zapewnia żadnej funkcjonalności). Wszystkie funkcje każdego z tych przycisków na pilocie są zaimplementowane w telewizorze.
Interfejs: Jest to „istniejący podmiot” warstwa pomiędzy
functionality
iconsumer
tej funkcjonalności. Sam interfejs nic nie robi. Po prostu przywołuje funkcjonalność.Teraz w zależności od tego, kim jest użytkownik, istnieją różne typy interfejsów.
Komendy interfejsu wiersza poleceń (CLI) to istniejące podmioty, konsument to użytkownik, a za nimi stoi funkcjonalność.
functionality:
moja funkcjonalność oprogramowania, która rozwiązuje jakiś cel, dla którego opisujemy ten interfejs.
existing entities:
polecenia
consumer:
użytkownikOkno graficznego interfejsu użytkownika (GUI) , przyciski itp. Są istniejącymi jednostkami, a konsument jest użytkownikiem, a funkcjonalność pozostaje w tyle.
functionality:
moja funkcjonalność oprogramowania, która rozwiązuje pewien problem, dla którego opisujemy ten interfejs.
existing entities:
okno, przyciski itp.
consumer:
użytkownikFunkcje interfejsu programowania aplikacji (a ściślej mówiąc) interfejsy (w programowaniu opartym na interfejsie) są istniejącymi jednostkami, tutaj konsumentem jest inny program, a nie użytkownik, i za tą warstwą znów stoi funkcjonalność.
functionality:
moja funkcjonalność oprogramowania, która rozwiązuje pewien problem, dla którego opisujemy ten interfejs.
existing entities:
funkcje, interfejsy (tablica funkcji).
consumer:
inny program / aplikacja.Application Binary Interface (ABI) Tutaj zaczyna się mój problem.
functionality:
???
existing entities:
???
consumer:
???
- Napisałem oprogramowanie w różnych językach i zapewniłem różnego rodzaju interfejsy (CLI, GUI i API), ale nie jestem pewien, czy kiedykolwiek dostarczyłem jakieś ABI.
ABI obejmują takie szczegóły, jak
- typ danych, rozmiar i wyrównanie;
- konwencja wywoływania, która kontroluje sposób przekazywania argumentów funkcji i pobierania wartości zwracanych;
- numery wywołań systemowych i sposób, w jaki aplikacja powinna nawiązywać połączenia systemowe z systemem operacyjnym;
Inne ABI standaryzują szczegóły, takie jak
- mangling nazwy C ++,
- propagacja wyjątków oraz
- konwencja wywoływania między kompilatorami na tej samej platformie, ale nie wymagają one zgodności między platformami.
Kto potrzebuje tych informacji? Proszę nie mówić o systemie operacyjnym. Znam programowanie asemblacyjne. Wiem, jak działa łączenie i ładowanie. Wiem dokładnie, co dzieje się w środku.
Dlaczego pojawiło się zniekształcanie nazw w C ++? Myślałem, że rozmawiamy na poziomie binarnym. Dlaczego przychodzą języki?
W każdym razie pobrałem [PDF] System V Application Binary Interface Edition 4.1 (1997-03-18), aby zobaczyć, co dokładnie zawiera. Cóż, większość z nich nie miała sensu.
Dlaczego zawiera dwa rozdziały (czwarty i piąty) opisujące format pliku ELF ? W rzeczywistości są to jedyne dwa znaczące rozdziały tej specyfikacji. Pozostałe rozdziały dotyczą „procesora”. W każdym razie myślałem, że to zupełnie inny temat. Nie mów, że specyfikacje formatu plików ELF to ABI. Zgodnie z definicją nie kwalifikuje się jako interfejs .
Wiem, skoro rozmawiamy na tak niskim poziomie, musi to być bardzo konkretne. Ale nie jestem pewien, w jaki sposób jest on specyficzny dla architektury „zestawu instrukcji (ISA)”?
Gdzie mogę znaleźć ABI Microsoft Windows?
Oto główne pytania, które mnie denerwują.
Odpowiedzi:
Jednym łatwym sposobem na zrozumienie „ABI” jest porównanie go z „API”.
Znasz już interfejs API. Jeśli chcesz korzystać z funkcji, powiedzmy, biblioteki lub systemu operacyjnego, programujesz w oparciu o interfejs API. Interfejs API składa się z typów danych / struktur, stałych, funkcji itp., Których można użyć w kodzie w celu uzyskania dostępu do funkcji tego zewnętrznego komponentu.
ABI jest bardzo podobny. Pomyśl o tym jako o skompilowanej wersji API (lub jako API na poziomie języka maszynowego). Podczas pisania kodu źródłowego uzyskuje się dostęp do biblioteki za pośrednictwem interfejsu API. Po skompilowaniu kodu aplikacja uzyskuje dostęp do danych binarnych w bibliotece za pośrednictwem ABI. Interfejs ABI definiuje struktury i metody, które będą używane przez skompilowaną aplikację do uzyskiwania dostępu do biblioteki zewnętrznej (podobnie jak interfejs API), tylko na niższym poziomie. Twój interfejs API określa kolejność przekazywania argumentów do funkcji. Twój ABI określa mechanikę tego, w jaki sposóbargumenty te są przekazywane (rejestry, stos itp.). Twój interfejs API określa, które funkcje są częścią twojej biblioteki. Twój ABI określa, w jaki sposób kod jest przechowywany w pliku biblioteki, dzięki czemu dowolny program korzystający z twojej biblioteki może zlokalizować żądaną funkcję i wykonać ją.
ABI są ważne, jeśli chodzi o aplikacje korzystające z bibliotek zewnętrznych. Biblioteki są pełne kodu i innych zasobów, ale twój program musi wiedzieć, jak znaleźć to, czego potrzebuje w pliku biblioteki. Twój ABI określa, w jaki sposób zawartość biblioteki jest przechowywana w pliku, a twój program używa ABI do przeszukiwania pliku i znajdowania tego, czego potrzebuje. Jeśli wszystko w twoim systemie jest zgodne z tym samym ABI, to każdy program może pracować z dowolnym plikiem biblioteki, bez względu na to, kto je utworzył. Linux i Windows używają różnych ABI, więc program Windows nie będzie wiedział, jak uzyskać dostęp do biblioteki skompilowanej dla Linuksa.
Czasami zmiany ABI są nieuniknione. W takim przypadku wszelkie programy korzystające z tej biblioteki nie będą działać, chyba że zostaną ponownie skompilowane w celu korzystania z nowej wersji biblioteki. Jeśli ABI ulegnie zmianie, ale interfejs API się nie zmieni, wówczas stara i nowa wersja biblioteki są czasami nazywane „kompatybilnymi ze źródłami”. Oznacza to, że chociaż program skompilowany dla jednej wersji biblioteki nie będzie działał z drugą wersją, kod źródłowy napisany dla jednej będzie działał dla drugiej, jeśli zostanie ponownie skompilowany.
Z tego powodu programiści starają się zachować stabilność ABI (aby zminimalizować zakłócenia). Utrzymanie stabilności ABI oznacza, że nie zmienia się interfejsów funkcji (zwracany typ i liczba, typy i kolejność argumentów), definicji typów danych lub struktur danych, zdefiniowanych stałych itp. Można dodawać nowe funkcje i typy danych, ale istniejące muszą pozostać to samo. Jeśli na przykład twoja biblioteka używa 32-bitowych liczb całkowitych do wskazania przesunięcia funkcji i przełączysz się na 64-bitowe liczby całkowite, to już skompilowany kod, który korzysta z tej biblioteki, nie będzie miał dostępu do tego pola (lub dowolnego po nim) poprawnie . Dostęp do elementów struktury danych zostaje przekształcony w adresy pamięci i przesunięcia podczas kompilacji, a jeśli struktura danych ulegnie zmianie,
ABI niekoniecznie jest czymś, co wyraźnie podasz, chyba że wykonujesz prace projektowe na bardzo niskim poziomie. Nie jest on również specyficzny dla języka, ponieważ (na przykład) aplikacja C i aplikacja Pascal mogą używać tego samego ABI po ich skompilowaniu.
Edytować:Jeśli chodzi o pytanie dotyczące rozdziałów dotyczących formatu pliku ELF w dokumentach SysV ABI: Powodem włączenia tej informacji jest to, że format ELF określa interfejs między systemem operacyjnym a aplikacją. Gdy powiesz systemowi operacyjnemu, aby uruchomił program, oczekuje, że program zostanie sformatowany w określony sposób i (na przykład) oczekuje, że pierwsza sekcja pliku binarnego będzie nagłówkiem ELF zawierającym pewne informacje w określonych przesunięciach pamięci. W ten sposób aplikacja przekazuje ważne informacje o sobie do systemu operacyjnego. Jeśli zbudujesz program w formacie binarnym innym niż ELF (takim jak a.out lub PE), system operacyjny, który oczekuje aplikacji w formacie ELF, nie będzie w stanie zinterpretować pliku binarnego ani uruchomić aplikacji.
IIRC, Windows obecnie używa formatu Portable Executable (lub PE). W sekcji „linki zewnętrzne” na tej stronie Wikipedii znajdują się linki z dodatkowymi informacjami na temat formatu PE.
Ponadto, jeśli chodzi o twoją notatkę o zmianie nazwy w C ++: Podczas lokalizowania funkcji w pliku biblioteki funkcja jest zwykle sprawdzana według nazwy. C ++ pozwala na przeciążanie nazw funkcji, więc sama nazwa nie wystarcza do zidentyfikowania funkcji. Kompilatory C ++ mają własne sposoby radzenia sobie z tym wewnętrznie, zwane manglingiem nazw . Interfejs ABI może zdefiniować standardowy sposób kodowania nazwy funkcji, aby programy zbudowane w innym języku lub kompilatorze mogły znaleźć to, czego potrzebują. Kiedy używasz
extern "c"
w programie C ++, instruujesz kompilator, aby używał standardowego sposobu rejestrowania nazw, który jest zrozumiały dla innego oprogramowania.źródło
Jeśli znasz się na asemblerze i jak to działa na poziomie systemu operacyjnego, jesteś zgodny z pewnym ABI. ABI reguluje takie rzeczy, jak przekazywanie parametrów, gdzie umieszczane są zwracane wartości. Na wielu platformach jest tylko jeden ABI do wyboru, w takich przypadkach ABI jest po prostu „jak rzeczy działają”.
Jednak ABI reguluje także takie rzeczy, jak układ klas / obiektów w C ++. Jest to konieczne, jeśli chcesz mieć możliwość przekazywania referencji do obiektów przez granice modułów lub jeśli chcesz mieszać kod skompilowany z różnymi kompilatorami.
Ponadto, jeśli masz 64-bitowy system operacyjny, który może wykonywać 32-bitowe pliki binarne, będziesz mieć różne ABI dla kodu 32- i 64-bitowego.
Zasadniczo każdy kod, który łączysz w tym samym pliku wykonywalnym, musi być zgodny z tym samym ABI. Jeśli chcesz komunikować się między kodem przy użyciu różnych ABI, musisz użyć jakiejś formy RPC lub protokołów serializacji.
Myślę, że zbyt mocno starasz się wcisnąć różne typy interfejsów w ustalony zestaw cech. Na przykład interfejs niekoniecznie musi być podzielony na konsumentów i producentów. Interfejs to tylko konwencja, dzięki której dwa byty wchodzą w interakcje.
ABI mogą być (częściowo) agnostyczne wobec ISA. Niektóre aspekty (takie jak konwencje wywoływania) zależą od ISA, podczas gdy inne aspekty (takie jak układ klasy C ++) nie.
Dobrze zdefiniowany ABI jest bardzo ważny dla osób piszących kompilatory. Bez dobrze zdefiniowanego ABI nie byłoby możliwe wygenerowanie interoperacyjnego kodu.
EDYCJA: Kilka uwag do wyjaśnienia:
źródło
Tak naprawdę wcale nie potrzebujesz ABI, jeśli ...
Uproszczone streszczenie:
ABI to zestaw reguł, które przestrzegają kompilatory i konsolidatory w celu skompilowania programu, aby działał poprawnie. ABI obejmują wiele tematów:
Przyglądając się konwencji zwoływania, którą uważam za rdzeń ABI:
Sama maszyna nie ma pojęcia „funkcji”. Kiedy piszesz funkcję w języku wysokiego poziomu, takim jak c, kompilator generuje wiersz kodu asemblera, takiego jak
_MyFunction1:
. Jest to etykieta , która ostatecznie zostanie przekształcona w asembler przez asemblera. Ta etykieta oznacza „początek” twojej „funkcji” w kodzie asemblera. W kodzie wysokiego poziomu, kiedy „wywołujesz” tę funkcję, tak naprawdę robisz to, że procesor przeskakuje na adres tej etykiety i kontynuuje wykonywanie tam.W ramach przygotowań do skoku kompilator musi wykonać kilka ważnych rzeczy. Konwencja wywoływania jest jak lista kontrolna, którą kompilator stosuje, aby wykonać wszystkie te czynności:
_MyFunction1:
). W tym momencie możesz uznać procesor za „w” swojej „funkcji”.Istnieje wiele różnych ABI / konwencji wywoływania. Niektóre główne to:
Oto świetna strona, która faktycznie pokazuje różnice w zestawie generowane podczas kompilacji dla różnych ABI.
Inną rzeczą, o której należy wspomnieć, jest to, że ABI jest nie tylko istotny w module wykonywalnym programu. Jest również używany przez linker, aby upewnić się, że Twój program wywołuje funkcje biblioteki poprawnie. Na komputerze działa wiele bibliotek współużytkowanych i dopóki kompilator wie, z czego korzysta każdy z nich, może poprawnie wywoływać funkcje bez wysadzania stosu.
Twój kompilator rozumiejący jak wywoływać funkcje biblioteczne jest niezwykle ważny. Na platformie hostowanej (czyli takiej, na której system operacyjny ładuje programy), Twój program nie może nawet mrugać bez wykonania wywołania jądra.
źródło
Interfejs binarny aplikacji (ABI) jest podobny do interfejsu API, ale funkcja nie jest dostępna dla dzwoniącego na poziomie kodu źródłowego. Dostępna jest tylko reprezentacja binarna.
ABI mogą być zdefiniowane na poziomie architektury procesora lub na poziomie systemu operacyjnego. Wskaźniki ABI są standardami, które muszą być przestrzegane przez fazę generatora kodu kompilatora. Standard jest ustalany albo przez system operacyjny, albo przez procesor.
Funkcjonalność: Zdefiniuj mechanizm / standard, aby wywołania funkcji były niezależne od języka implementacji lub konkretnego kompilatora / linkera / zestawu narzędzi. Podaj mechanizm, który pozwala JNI lub interfejs Python-C itp.
Istniejące podmioty: funkcje w postaci kodu maszynowego.
Konsument: inna funkcja (w tym jedna w innym języku, skompilowana przez inny kompilator lub połączona przez inny linker).
źródło
Funkcjonalność: zestaw umów wpływających na kompilator, autorów asemblerów, konsolidator i system operacyjny. Umowy określają, w jaki sposób są układane funkcje, gdzie parametry są przekazywane, jak parametry są przekazywane, jak funkcja zwraca działanie. Są one generalnie specyficzne dla krotki (architektura procesorów, system operacyjny).
Istniejące jednostki: układ parametrów, semantyka funkcji, przydział rejestrów. Na przykład architektury ARM mają wiele ABI (APCS, EABI, GNU-EABI, nie wspominając o wielu historycznych przypadkach) - użycie mieszanego ABI spowoduje, że kod po prostu nie będzie działał podczas wywoływania ponad granicami.
Konsument: kompilator, autorzy asemblera, system operacyjny, architektura specyficzna dla procesora.
Kto potrzebuje tych informacji? Kompilator, piszące asemblery, konsolidatory generujące kod (lub wymagania wyrównania), system operacyjny (obsługa przerwań, interfejs syscall). Jeśli zajmowałeś się programowaniem asemblera, byłeś zgodny z ABI!
Cangowanie nazw w C ++ jest szczególnym przypadkiem - jest to problem związany z linkerem i dynamicznym linkerem - jeśli mangling nazwy nie jest ustandaryzowany, wówczas dynamiczne linkowanie nie będzie działać. Odtąd C ++ ABI nazywa się właśnie tak, C ++ ABI. To nie jest problem z poziomem linkera, ale problem z generowaniem kodu. Gdy masz już plik binarny C ++, nie jest możliwe uczynienie go kompatybilnym z innym C ++ ABI (zmiana nazwy, obsługa wyjątków) bez ponownej kompilacji ze źródła.
ELF to format pliku przeznaczony do użycia modułu ładującego i dynamicznego linkera. ELF to format kontenera na kod binarny i dane, i jako taki określa ABI fragmentu kodu. Nie uważałbym, że ELF jest ABI w ścisłym tego słowa znaczeniu, ponieważ pliki wykonywalne PE nie są ABI.
Wszystkie ABI są specyficzne dla zestawu instrukcji. ARM ABI nie będzie miał sensu na procesorze MSP430 lub x86_64.
Windows ma kilka ABI - na przykład fastcall i stdcall to dwa najczęściej używane ABI. Syscall ABI znów jest inny.
źródło
Pozwól mi przynajmniej odpowiedzieć na część twojego pytania. Na przykładzie tego, jak Linux ABI wpływa na wywołania systemowe i dlaczego jest to przydatne.
Systemcall to sposób, w jaki program przestrzeni użytkownika może poprosić przestrzeń jądra o coś. Działa poprzez umieszczenie kodu numerycznego wywołania i argumentu w pewnym rejestrze i wywołanie przerwania. Następnie następuje przełączenie do przestrzeni jądra, a jądro sprawdza kod numeryczny i argument, obsługuje żądanie, umieszcza wynik z powrotem w rejestrze i uruchamia przełącznik z powrotem do przestrzeni użytkownika. Jest to potrzebne na przykład, gdy aplikacja chce przydzielić pamięć lub otworzyć plik (syscalls „brk” i „open”).
Teraz syscalls mają krótkie nazwy „brk” itp. I odpowiadające im kody operacyjne, które są zdefiniowane w pliku nagłówkowym specyficznym dla systemu. Dopóki te kody operacyjne pozostają takie same, możesz uruchamiać te same skompilowane programy użytkownika z różnymi zaktualizowanymi jądrami bez konieczności ponownej kompilacji. Mamy więc interfejs używany przez wstępnie skompilowane pliki binarne, stąd ABI.
źródło
Aby wywoływać kod we współdzielonych bibliotekach lub kod wywoływać między jednostkami kompilacyjnymi, plik obiektowy musi zawierać etykiety wywołań. C ++ zmienia nazwy etykiet metod, aby wymusić ukrywanie danych i pozwolić na przeciążenie metod. Dlatego nie można mieszać plików z różnych kompilatorów C ++, chyba że jawnie obsługują ten sam ABI.
źródło
Najlepszym sposobem na rozróżnienie ABI i API jest wiedzieć, dlaczego i do czego służy:
Dla x86-64 jest na ogół jeden ABI (a dla x86 32-bit jest inny zestaw):
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 podążają za nim z pewnymi niewielkimi zmianami. A Windows x64 ma swój własny ABI:
http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
Znając ABI i zakładając, że podąża za nim inny kompilator, binarne teoretycznie wiedzą, jak się nawzajem wywoływać (w szczególności API bibliotek) i przekazywać parametry przez stos lub rejestry itp. Lub jakie rejestry zostaną zmienione po wywołaniu funkcji itp. Zasadniczo wiedza ta pomoże w integracji oprogramowania. Znając kolejność układu rejestrów / stosu, mogę bez problemu łączyć ze sobą różne programy napisane w złożeniach.
Ale API są różne:
Są to nazwy funkcji wysokiego poziomu, ze zdefiniowanym argumentem, tak że jeśli różne elementy oprogramowania budują się przy użyciu tych API, MOGĄ być w stanie nawzajem się nawiązywać. Ale należy przestrzegać dodatkowego wymogu SAMEGO ABI.
Na przykład system Windows był zgodny z interfejsem API POSIX:
https://en.wikipedia.org/wiki/Windows_Services_for_UNIX
https://en.wikipedia.org/wiki/POSIX
Linux jest również zgodny z POSIX. Ale plików binarnych nie można po prostu przenieść i uruchomić natychmiast. Ponieważ jednak użyli tego samego NAMES w interfejsie API zgodnym z POSIX, możesz wziąć to samo oprogramowanie w C, ponownie skompilować je w innym systemie operacyjnym i natychmiast uruchomić.
Interfejsy API mają ułatwić integrację oprogramowania - etap wstępnej kompilacji. Po kompilacji oprogramowanie może wyglądać zupełnie inaczej - jeśli ABI są inne.
ABI mają na celu zdefiniowanie dokładnej integracji oprogramowania na poziomie binarnym / asemblacyjnym.
źródło
SYS_execve
to 11 na 32- bitowym Linuksie, ale 59 na FreeBSD.personality(2)
można ustawićPER_BSD
. Myślę Pamiętampersonality(PER_LINUX)
wstrace
wyjściu cały czas, ale nowoczesny 64-bitowe pliki binarne Linux nie rób tego więcej.Przykładowa biblioteka ABI z Linuksem współdzielonym w minimalnym zakresie
W kontekście bibliotek współdzielonych najważniejszą implikacją „posiadania stabilnego ABI” jest to, że nie trzeba ponownie kompilować programów po zmianach w bibliotece.
Na przykład:
jeśli sprzedajesz bibliotekę współdzieloną, oszczędzasz użytkownikom irytujące ponowne kompilowanie wszystkiego, co zależy od twojej biblioteki dla każdej nowej wersji
jeśli sprzedajesz program o zamkniętym źródle, który zależy od biblioteki współdzielonej obecnej w dystrybucji użytkownika, możesz wypuścić i przetestować mniej prebuiltów, jeśli masz pewność, że ABI jest stabilny w niektórych wersjach docelowego systemu operacyjnego.
Jest to szczególnie ważne w przypadku standardowej biblioteki C, do której prowadzi wiele programów w twoim systemie.
Teraz chcę podać minimalny konkretny przykład tego działania.
main.c
mylib.c
mylib.h
Kompiluje się i działa poprawnie z:
Załóżmy teraz, że dla v2 biblioteki chcemy dodać nowe pole do
mylib_mystruct
wywołanegonew_field
.Jeśli dodaliśmy pole wcześniej
old_field
jak w:i przebudował bibliotekę, ale nie
main.out
, to nie powiedzie się!Wynika to z faktu, że wiersz:
wygenerował zestaw, który próbuje uzyskać dostęp do pierwszej
int
struktury, która jest teraznew_field
zamiast oczekiwanejold_field
.Dlatego ta zmiana złamała ABI.
Jeśli jednak dodajemy
new_field
poold_field
:wtedy stary wygenerowany zestaw nadal uzyskuje dostęp do pierwszej
int
struktury, a program nadal działa, ponieważ utrzymaliśmy stabilność ABI.Oto w pełni zautomatyzowana wersja tego przykładu na GitHub .
Innym sposobem na utrzymanie stabilnego ABI byłoby traktowanie go
mylib_mystruct
jako nieprzezroczystej struktury i dostęp do jego pól tylko poprzez pomocników metod. Ułatwia to utrzymanie stabilności ABI, ale wiązałoby się to z dodatkowym obciążeniem wydajności, ponieważ wykonujemy więcej wywołań funkcji.API vs ABI
W poprzednim przykładzie warto zauważyć, że dodanie
new_field
wcześniejszegoold_field
tylko zepsuło ABI, ale nie API.Oznacza to, że gdybyśmy skompilowali nasz
main.c
program z biblioteką, działałby niezależnie.Zerwalibyśmy również interfejs API, gdybyśmy zmienili na przykład podpis funkcji:
ponieważ w takim przypadku
main.c
przestałby się całkowicie kompilować.Semantyczny interfejs API a programujący interfejs API
Możemy również klasyfikować zmiany API w trzecim typie: zmiany semantyczne.
Semantyczny interfejs API jest zwykle opisem w języku naturalnym tego, co powinien robić interfejs API, zwykle zawartym w dokumentacji interfejsu API.
Możliwe jest zatem zerwanie semantycznego interfejsu API bez naruszenia samej kompilacji programu.
Na przykład, gdybyśmy zmodyfikowali
do:
wtedy nie spowodowałoby to uszkodzenia ani programistycznego API, ani ABI, ale
main.c
semantyczny API by się zepsuł .Istnieją dwa sposoby programowego sprawdzenia interfejsu API umowy:
weryfikacja formalna . Trudniejsze, ale daje matematyczny dowód poprawności, zasadniczo ujednolicając dokumentację i testy w sposób „ludzki” / weryfikowalny przez maszynę! Dopóki oczywiście nie ma błędu w twoim formalnym opisie ;-)
Ta koncepcja jest ściśle związana z formalizacją samej matematyki: /math/53969/what-does-formal-mean/3297537#3297537
Lista wszystkiego, co psuje ABI bibliotek współużytkowanych w C / C ++
DO ZROBIENIA: znajdź / stwórz ostateczną listę:
Przykład minimalnego uruchamiania Java
Co to jest zgodność binarna w Javie?
Testowane w Ubuntu 18.10, GCC 8.2.0.
źródło
ABI musi być spójny między dzwoniącym a odbiorcą, aby mieć pewność, że połączenie się powiedzie. Używanie stosu, używanie rejestrowania, pop-up stosu na koniec rutyny. Wszystkie te są najważniejszymi częściami ABI.
źródło
Podsumowanie
Istnieją różne interpretacje i silne opinie na temat konkretnej warstwy, która definiuje ABI (binarny interfejs aplikacji).
Moim zdaniem ABI to subiektywna konwencja tego, co uważa się za daną / platformę dla konkretnego API. ABI to „reszta” konwencji, które „nie zmienią się” dla konkretnego interfejsu API lub które zostaną rozwiązane przez środowisko wykonawcze: narzędzia wykonawcze, narzędzia, konsolidatory, kompilatory, jvm i system operacyjny.
Definiowanie interfejsu : ABI, API
Jeśli chcesz użyć biblioteki takiej jak joda-time, musisz zadeklarować zależność od
joda-time-<major>.<minor>.<patch>.jar
. Biblioteka postępuje zgodnie z najlepszymi praktykami i używa Wersji semantycznej . Określa to zgodność API na trzech poziomach:Aby móc korzystać z nowej wersji głównej tej samej biblioteki, należy przestrzegać wielu innych konwencji:
Przykłady
Studium przypadku Java
Na przykład Java ustandaryzowała wszystkie te konwencje, nie w narzędziu, ale w formalnej specyfikacji JVM. Specyfikacja pozwoliła innym dostawcom udostępnić inny zestaw narzędzi, które mogą wyświetlać kompatybilne biblioteki.
Java udostępnia dwa inne interesujące studia przypadków dla ABI: wersje Scala i maszynę wirtualną Dalvik .
Maszyna wirtualna Dalvik złamała ABI
Dalvik VM potrzebuje innego typu kodu bajtowego niż kod bajtowy Java. Biblioteki Dalvik są uzyskiwane przez konwersję kodu bajtowego Java (z tym samym API) dla Dalvik. W ten sposób możesz uzyskać dwie wersje tego samego API: zdefiniowane przez oryginał
joda-time-1.7.2.jar
. Możemy do mnie zadzwonićjoda-time-1.7.2.jar
ijoda-time-1.7.2-dalvik.jar
. Używają innego ABI, który jest przeznaczony dla standardowych Java VMS zorientowanych na stos: Oracle, IBM, Open Java lub dowolny inny; a drugim ABI jest ten wokół Dalviku.Kolejne wydania Scali są niezgodne
Scala nie ma binarnej zgodności między mniejszymi wersjami Scala: 2.X. Z tego powodu ten sam interfejs API „io.reactivex” %% „rxscala”% „0.26.5” ma trzy wersje (w przyszłości więcej): dla Scali 2.10, 2.11 i 2.12. Co się zmieniło? Na razie nie wiem , ale pliki binarne nie są kompatybilne. Prawdopodobnie najnowsze wersje dodają rzeczy, które sprawiają, że biblioteki nie nadają się do użytku na starych maszynach wirtualnych, prawdopodobnie rzeczy związane z konwencjami łączenia / nazewnictwa / parametrów.
Kolejne wydania Java są niezgodne
Java ma również problemy z głównymi wersjami JVM: 4,5,6,7,8,9. Oferują one tylko zgodność wsteczną. Jvm9 wie, jak uruchomić kod skompilowany / ukierunkowany (
-target
opcja javac ) dla wszystkich innych wersji, podczas gdy JVM 4 nie wie, jak uruchomić kod ukierunkowany na JVM 5. Wszystko to, gdy masz jedną bibliotekę joda. Ta niezgodność leci poniżej radaru dzięki różnym rozwiązaniom:Dlaczego zacząłem od definicji API?
API i ABI to tylko konwencje definiowania zgodności. Dolne warstwy są ogólne w odniesieniu do mnogości semantyki wysokiego poziomu. Dlatego łatwo jest wprowadzić pewne konwencje. Pierwszy rodzaj konwencji dotyczy wyrównania pamięci, kodowania bajtów, konwencji wywoływania, dużych i małych kodowań endianów itp. Na ich wierzchu znajdują się konwencje wykonywalne, takie jak inne, konwencje łączenia, kod bajtów pośrednich, taki jak używany przez Javę lub LLVM IR używany przez GCC. Po trzecie, masz konwencje, jak znaleźć biblioteki i jak je załadować (zobacz moduły ładujące Java). Idąc coraz wyżej w koncepcjach, masz nowe konwencje, które uznajesz za dane. Dlatego nie przeszli do wersji semantycznej .wersja. Możemy zmienić wersję semantyczną za pomocą
<major>-<minor>-<patch>-<platform/ABI>
. To jest to, co rzeczywiście dzieje się już: Platforma jest jużrpm
,dll
,jar
(JVM kodu bajtowego),war
(JVM + serwer WWW)apk
,2.11
(specyficzna wersja Scala) i tak dalej. Kiedy mówisz APK, mówisz już o określonej części ABI swojego API.Interfejs API można przenosić do różnych ABI
Najwyższy poziom abstrakcji (źródła napisane w oparciu o najwyższy API mogą być ponownie skompilowane / przeniesione do dowolnej innej abstrakcji niższego poziomu.
Powiedzmy, że mam kilka źródeł dla rxscala. Jeśli narzędzia Scala zostaną zmienione, mogę je ponownie skompilować. Jeśli JVM ulegnie zmianie, mógłbym mieć automatyczne konwersje ze starej maszyny na nową bez zawracania sobie głowy koncepcjami wysokiego poziomu. Przeniesienie może być trudne, ale pomoże każdemu innemu klientowi. Jeśli nowy system operacyjny zostanie utworzony przy użyciu zupełnie innego kodu asemblera, można utworzyć tłumacz.
Interfejsy API przeniesione między językami
Istnieją interfejsy API, które są przenoszone w wielu językach, takich jak strumienie reaktywne . Zasadniczo definiują odwzorowania na określone języki / platformy. Argumentowałbym, że API jest specyfikacją główną formalnie zdefiniowaną w języku ludzkim lub nawet w konkretnym języku programowania. Wszystkie pozostałe „mapowania” są w pewnym sensie ABI, w innym przypadku bardziej API niż zwykłe ABI. To samo dzieje się z interfejsami REST.
źródło
W filozofii i w krótkim, tylko rzeczy o naturze mogą się dogadać, a ABI może być postrzegane jako rodzaj których prace oprogramowanie rzeczy razem.
źródło
Próbowałem też zrozumieć ABI, a odpowiedź JesperE była bardzo pomocna.
Z bardzo prostej perspektywy możemy spróbować zrozumieć ABI, biorąc pod uwagę zgodność binarną.
KDE wiki definiuje bibliotekę jako kompatybilną binarnie „jeśli program połączony dynamicznie z poprzednią wersją biblioteki nadal działa z nowszymi wersjami biblioteki bez konieczności ponownej kompilacji”. Aby uzyskać więcej informacji o łączeniu dynamicznym, zobacz Łączenie statyczne vs. łączenie dynamiczne
Teraz spróbujmy przyjrzeć się tylko najbardziej podstawowym aspektom niezbędnym do zapewnienia zgodności binarnej biblioteki (zakładając, że nie ma żadnych zmian w kodzie źródłowym biblioteki):
Jasne, istnieje wiele innych szczegółów, ale to głównie to, co obejmuje również ABI.
Dokładniej, aby odpowiedzieć na twoje pytanie, z powyższego możemy wywnioskować:
Mam nadzieję że to pomoże!
źródło
Interfejs binarny aplikacji (ABI)
Funkcjonalność:
Istniejące podmioty:
konsument:
Są one potrzebne każdemu, kto musi zadbać o to, aby budowane łańcuchy narzędzi działały jako całość. Jeśli napiszesz jeden moduł w języku asemblera, inny w Pythonie, a zamiast własnego programu ładującego chcesz używać systemu operacyjnego, wówczas moduły „aplikacji” działają ponad „binarnymi” granicami i wymagają zgody takiego „interfejsu”.
Mangowanie nazw w C ++, ponieważ pliki aplikacji z różnych języków wysokiego poziomu mogą wymagać połączenia w aplikacji. Rozważ użycie standardowej biblioteki GCC do wykonywania wywołań systemowych do systemu Windows zbudowanego za pomocą Visual C ++.
ELF jest jednym z możliwych oczekiwań łącznika z pliku obiektowego do interpretacji, chociaż JVM może mieć inny pomysł.
W przypadku aplikacji Windows RT Store spróbuj wyszukać ARM ABI, jeśli naprawdę chcesz, aby niektóre narzędzia do budowania działały razem.
źródło
Termin ABI jest używany w odniesieniu do dwóch różnych, ale powiązanych pojęć.
Mówiąc o kompilatorach, odnosi się do reguł używanych do tłumaczenia konstruktów poziomu źródłowego na konstrukcje binarne. Jak duże są typy danych? jak działa stos? jak przekazać parametry do funkcji? które rejestry powinny zostać zapisane przez dzwoniącego kontra odbiorcę?
Mówiąc o bibliotekach, odnosi się do interfejsu binarnego prezentowanego przez skompilowaną bibliotekę. Interfejs ten jest wynikiem wielu czynników, w tym kodu źródłowego biblioteki, reguł używanych przez kompilator oraz w niektórych przypadkach definicji pobranych z innych bibliotek.
Zmiany w bibliotece mogą uszkodzić ABI bez uszkodzenia API. Rozważmy na przykład bibliotekę z interfejsem podobnym do.
a programista aplikacji pisze kod podobny do
Programista aplikacji nie dba o rozmiar ani układ FOO, ale plik binarny aplikacji kończy się na ustalonym rozmiarze foo. Jeśli programista biblioteki doda dodatkowe pole do foo, a ktoś użyje nowego pliku binarnego biblioteki ze starym plikiem binarnym aplikacji, biblioteka może uzyskać dostęp do pamięci poza granicami.
OTOH, jeśli autor biblioteki zaprojektował swój API podobny.
a programista aplikacji pisze kod podobny do
Wówczas aplikacja binarna nie musi wiedzieć nic o strukturze FOO, które wszystkie mogą być ukryte w bibliotece. Cena, którą za to płacisz, polega jednak na operacjach sterty.
źródło
ABI
-Application Binary Interface
dotyczy komunikacji kodu maszynowego w czasie wykonywania między dwiema częściami programu binarnego, takimi jak - aplikacja, biblioteka, system operacyjny ...ABI
opisuje sposób zapisywania obiektów w pamięci i wywoływania funkcji (calling convention
)Dobrym przykładem API i ABI jest ekosystem iOS z językiem Swift .
Application
- Podczas tworzenia aplikacji w różnych językach. Na przykład możesz utworzyć aplikację za pomocąSwift
iObjective-C
[Mixing Swift i Objective-C]Application - OS
- środowisko wykonawcze -Swift runtime
istandard libraries
są częścią systemu operacyjnego i nie powinny być dołączane do każdego pakietu (np. aplikacji, frameworka). Jest taki sam jak w przypadku Objective-CLibrary
-Module Stability
przypadek - czas kompilacji - będziesz mógł zaimportować framework, który został zbudowany z innej wersji kompilatora Swift. Oznacza to, że bezpiecznym jest tworzenie pliku binarnego o zamkniętym źródle (przed kompilacją), który będzie.swiftinterface
używany przez inną wersję kompilatora ( używanego z.swiftmodule
) i nie otrzymaszLibrary
-Library Evolution
skrzynia[API vs ABI]
źródło