Co to jest binarny interfejs aplikacji (ABI)?


493

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 functionalityi consumertej 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żytkownik

Okno 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żytkownik

Funkcje 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.

Wikipedia mówi:

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ą.


7
„Proszę nie mówić, OS” Kompilatory muszą znać ABI. Linkery muszą znać ABI. Jądro musi znać ABI, aby ustawić program w pamięci RAM, aby działał poprawnie. Jeśli chodzi o C ++, patrz poniżej, celowo zamienia etykiety w bełkot z powodu przeciążenia i metod prywatnych, a linker i każdy inny kompilator muszą mieć kompatybilną nazwę mangling do pracy z nim, innymi słowy ten sam ABI.
Justin Smith

8
Myślę, że pytanie jest takie jasne; dokładnie opisujący oczekiwany format odpowiedzi, a jednocześnie nie zadowalającą odpowiedź, którą można zaakceptować.
legends2k

3
@ legends2k Uważam, że OP rzeczywiście wie, czym jest ABI, ale nie zdaje sobie z tego sprawy. Zdecydowana większość programistów nigdy nie zaprojektuje ani nie zapewni ABI, ponieważ jest to zadanie projektantów systemów operacyjnych / platform.
JesperE

4
@JesperE: Zgadzam się z twoim punktem widzenia. Ale prawdopodobnie OP chce wiedzieć to wyraźnie, w formacie, który uważa za odpowiedni, nawet jeśli nie będzie musiał podać ABI.
legends2k

2
Byłem ignorantem. Ostatnio podczas pracy z tymi wszystkimi rzeczami. Uświadomiłem sobie, czym w rzeczywistości jest ABI. Tak, zgadzam się, że mój szablon jest wadliwy. Nie należy dopasowywać ABI do mojego szablonu. Dzięki @ JasperE. Po prostu zrozumienie Twojej odpowiedzi wymagało doświadczenia zawodowego.
pazury

Odpowiedzi:


534

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.


2
@bta, Dzięki za świetną odpowiedź. Czy konwencja wywoływania jest rodzajem ABI? Dzięki
camino

37
Niezła odpowiedź. Tyle że to nie jest ABI. ABI to zestaw reguł, które określają konwencję wywoływania, oraz zasady układania struktur. Pascal przekazuje argumenty na stos w odwrotnej kolejności z aplikacji C, więc kompilatory pascal i C NIE kompilują się do tego samego ABI. Odpowiednie standardy dla kompilatorów C i Pascal domyślnie zapewniają, że tak będzie. Kompilatory w C ++ nie mogą zdefiniować „standardowego” sposobu zmieniania nazw, ponieważ nie ma standardowego sposobu. Konwencje przekształcania nazw w C ++ nie były kompatybilne między kompilatorami C ++, gdy w Windows istniały konkurencyjne kompilatory C ++.
Robin Davies,


1
@RobinDavies: Na platformach, na których kompilatory Pascal wywoływałyby funkcje pop argumenty podane przez ich osoby wywołujące, kompilatory C ogólnie określałyby środki, za pomocą których programista mógłby wskazać, że określone funkcje powinny używać, lub należy się spodziewać, że używają tych samych konwencji wywoływania, jak Kompilatory Pascal, mimo że kompilatory C zazwyczaj domyślnie stosują konwencję, w której wywoływane funkcje pozostawiają na stosie wszystko, co tam umieszczają ich wywołujący.
supercat

Czy mogę powiedzieć, że pliki obj generowane przez kompilator C zawierają ABI?
Mitu Raj

144

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:

  • „Binarny” w ABI nie wyklucza użycia ciągów lub tekstu. Jeśli chcesz połączyć bibliotekę DLL eksportującą klasę C ++, gdzieś w niej metody i podpisy typów muszą być zakodowane. Właśnie tutaj pojawia się mangling C ++.
  • Powodem, dla którego nigdy nie podałeś ABI, jest to, że zdecydowana większość programistów nigdy tego nie zrobi. ABI są dostarczane przez te same osoby projektujące platformę (tj. System operacyjny), a bardzo niewielu programistów kiedykolwiek będzie miało przywilej zaprojektowania powszechnie używanego ABI.

Wcale nie jestem przekonany, że mój szablon jest wadliwy. Ponieważ wszędzie tam, gdzie ten szablon interfejsu jest prawdziwy. Tak, tak, chcę, że ABI również pasuje do tego szablonu, ale to nie wszystko. WAŻNE jest to, że nadal nie rozumiem. Nie wiem, czy jestem taki głupi czy coś innego, ale to po prostu nie przychodzi mi do głowy. Nie jestem w stanie zrozumieć odpowiedzi i artykułu na wiki.
pazury

2
@jesperE, „ABI reguluje takie rzeczy, jak przekazywanie parametrów, gdzie są zwracane wartości.” odnosi się do „cdecl, stdcall, fastcall, pascal”, prawda?
camino

3
Tak. Właściwa nazwa to „konwencja wywoływania”, która jest częścią ABI. en.wikipedia.org/wiki/X86_calling_conventions
JesperE

4
to prawidłowa i precyzyjna odpowiedź bez gadatliwości (raczej szum )!
Nawaz

Polecam napisać trochę asemblera. Pomoże to ludziom zrozumieć ABI w bardziej namacalny sposób.
KunYu Tsai

40

Tak naprawdę wcale nie potrzebujesz ABI, jeśli ...

  • Twój program nie ma funkcji i--
  • Twój program to pojedynczy plik wykonywalny, który działa sam (tj. System osadzony), gdzie jest dosłownie jedyną uruchomioną rzeczą i nie musi rozmawiać z niczym innym.

Uproszczone streszczenie:

API: „Oto wszystkie funkcje, które możesz wywołać”.

ABI: „Oto jak wywołać funkcję.”

ABI to zestaw reguł, które przestrzegają kompilatory i konsolidatory w celu skompilowania programu, aby działał poprawnie. ABI obejmują wiele tematów:

  • Prawdopodobnie największą i najważniejszą częścią ABI jest standard wywoływania procedur zwany czasem „konwencją wywoływania”. Konwencje wywoływania standaryzują sposób, w jaki „funkcje” są tłumaczone na kod asemblera.
  • ABI decydują również o tym, w jaki sposób powinny być reprezentowane nazwy ujawnionych funkcji w bibliotekach, aby inny kod mógł wywoływać te biblioteki i wiedzieć, jakie argumenty należy przekazać. Nazywa się to „manglingiem nazw”.
  • Wskaźniki ABI określają również, jakiego rodzaju typy danych mogą być używane, jak muszą być wyrównane oraz inne szczegóły niskiego poziomu.

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:

  • Po pierwsze, kompilator wstawia trochę kodu asemblera, aby zapisać aktualny adres, aby po zakończeniu „funkcji” procesor mógł wskoczyć z powrotem we właściwe miejsce i kontynuować wykonywanie.
  • Następnie kompilator generuje kod asemblera do przekazania argumentów.
    • Niektóre konwencje wywoływania nakazują umieszczanie argumentów na stosie ( oczywiście w określonej kolejności ).
    • Inne konwencje nakazują umieszczanie argumentów w poszczególnych rejestrach ( oczywiście w zależności od ich typów danych ).
    • Jeszcze inne konwencje nakazują stosowanie określonej kombinacji stosu i rejestrów.
  • Oczywiście, jeśli wcześniej było coś ważnego w tych rejestrach, wartości te są teraz nadpisywane i utracone na zawsze, więc niektóre konwencje wywoływania mogą nakazać, aby kompilator zapisał niektóre z tych rejestrów przed umieszczeniem w nich argumentów.
  • Teraz kompilator wstawia instrukcję skoku mówiąc CPU, aby przejść do etykiety, którą wcześniej utworzył ( _MyFunction1:). W tym momencie możesz uznać procesor za „w” swojej „funkcji”.
  • Na końcu funkcji kompilator umieszcza kod asemblera, który spowoduje, że CPU zapisze wartość zwracaną w odpowiednim miejscu. Konwencja wywoływania będzie określać, czy zwracana wartość powinna być umieszczana w określonym rejestrze (w zależności od jego typu), czy na stosie.
  • Teraz czas na porządki. Konwencja wywoływania określa, gdzie kompilator umieszcza kod zestawu czyszczenia.
    • Niektóre konwencje mówią, że osoba dzwoniąca musi wyczyścić stos. Oznacza to, że po wykonaniu „funkcji” i przejściu procesora z powrotem do miejsca, w którym był wcześniej, następnym kodem do wykonania powinien być bardzo specyficzny kod czyszczenia.
    • Inne konwencje mówią, że niektóre części kodu czyszczenia powinny znajdować się na końcu „funkcji” przed powrotem do poprzedniej wersji.

Istnieje wiele różnych ABI / konwencji wywoływania. Niektóre główne to:

  • W przypadku procesora x86 lub x86-64 (środowisko 32-bitowe):
    • CDECL
    • STDCALL
    • FASTCALL
    • WEKTOR
    • ZGŁOSZENIE
  • W przypadku procesora x86-64 (środowisko 64-bitowe):
    • SYSTEMV
    • MSNATIVE
    • WEKTOR
  • Dla procesora ARM (32-bit)
    • AAPCS
  • Dla procesora ARM (64-bit)
    • AAPCS64

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.


19

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).


Dlaczego architekturę definiuje ABI? Dlaczego różne systemy operacyjne w tej samej architekturze nie byłyby w stanie zdefiniować różnych ABI?
Andreas Haferburg

10

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.


9

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.


4

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.


4

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.


Konwencja wywoływania x86-64 systemu Windows nie korzysta z konwencji wywoływania SysV, z której korzystają wszystkie inne systemy operacyjne x86-64. Linux / OS X / FreeBSD mają tę samą konwencję wywoływania, ale nie mają pełnego ABI. ABI systemu operacyjnego zawiera numery wywołań systemowych. np. freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/… mówi, że SYS_execveto 11 na 32- bitowym Linuksie, ale 59 na FreeBSD.
Peter Cordes,

dzięki za komentarz, zmodyfikowałem swój komentarz, aby lepiej odpowiadał na różnicę między ABI a API.
Peter Teoh

Nadal brakuje Ci różnicy między konwencją wywoływania a pełnym ABI (wywołania systemowe i wszystko). Możesz uruchomić niektóre pliki binarne FreeBSD w systemie Linux, ponieważ Linux (jądro) zapewnia warstwę kompatybilności z FreeBSD. Nawet wtedy jest to ograniczone do plików binarnych, które nie próbują używać żadnej części FreeBSD ABI, której Linux nie zapewnia. (np. każde wywołanie systemowe tylko dla FreeBSD). Kompatybilny z ABI oznacza, że ​​możesz uruchamiać ten sam plik binarny na obu systemach, nie tylko że będą się kompilować podobnie.
Peter Cordes

„Warstwa kompatybilności FreeBSD”, nigdy o tym nie słyszałem. Czy możesz wskazać odpowiedni kod źródłowy jądra systemu Linux? Ale istnieje odwrotność : freebsd.org/doc/en_US.ISO8859-1/books/handbook/linuxemu.html .
Peter Teoh

Nie używam tego. Myślałem, że coś takiego istnieje, ale może już nie. tldp.org/HOWTO/Linux+FreeBSD-6.html mówi, że jest nieobsługiwany, a to howto pochodzi z 2000 roku. xD. unix.stackexchange.com/questions/172038/... potwierdza, że ​​został porzucony i nigdy nie został ponownie wykonany (ponieważ nikt nie chciał tego wystarczająco mocno, aby to zrobić). personality(2)można ustawić PER_BSD. Myślę Pamiętam personality(PER_LINUX)w stracewyjściu cały czas, ale nowoczesny 64-bitowe pliki binarne Linux nie rób tego więcej.
Peter Cordes

4

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

#include <assert.h>
#include <stdlib.h>

#include "mylib.h"

int main(void) {
    mylib_mystruct *myobject = mylib_init(1);
    assert(myobject->old_field == 1);
    free(myobject);
    return EXIT_SUCCESS;
}

mylib.c

#include <stdlib.h>

#include "mylib.h"

mylib_mystruct* mylib_init(int old_field) {
    mylib_mystruct *myobject;
    myobject = malloc(sizeof(mylib_mystruct));
    myobject->old_field = old_field;
    return myobject;
}

mylib.h

#ifndef MYLIB_H
#define MYLIB_H

typedef struct {
    int old_field;
} mylib_mystruct;

mylib_mystruct* mylib_init(int old_field);

#endif

Kompiluje się i działa poprawnie z:

cc='gcc -pedantic-errors -std=c89 -Wall -Wextra'
$cc -fPIC -c -o mylib.o mylib.c
$cc -L . -shared -o libmylib.so mylib.o
$cc -L . -o main.out main.c -lmylib
LD_LIBRARY_PATH=. ./main.out

Załóżmy teraz, że dla v2 biblioteki chcemy dodać nowe pole do mylib_mystructwywołanego new_field.

Jeśli dodaliśmy pole wcześniej old_fieldjak w:

typedef struct {
    int new_field;
    int old_field;
} mylib_mystruct;

i przebudował bibliotekę, ale nie main.out, to nie powiedzie się!

Wynika to z faktu, że wiersz:

myobject->old_field == 1

wygenerował zestaw, który próbuje uzyskać dostęp do pierwszej intstruktury, która jest teraz new_fieldzamiast oczekiwanej old_field.

Dlatego ta zmiana złamała ABI.

Jeśli jednak dodajemy new_fieldpo old_field:

typedef struct {
    int old_field;
    int new_field;
} mylib_mystruct;

wtedy stary wygenerowany zestaw nadal uzyskuje dostęp do pierwszej intstruktury, 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_mystructjako 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_fieldwcześniejszego old_fieldtylko zepsuło ABI, ale nie API.

Oznacza to, że gdybyśmy skompilowali nasz main.cprogram z biblioteką, działałby niezależnie.

Zerwalibyśmy również interfejs API, gdybyśmy zmienili na przykład podpis funkcji:

mylib_mystruct* mylib_init(int old_field, int new_field);

ponieważ w takim przypadku main.cprzestał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

myobject->old_field = old_field;

do:

myobject->old_field = old_field + 1;

wtedy nie spowodowałoby to uszkodzenia ani programistycznego API, ani ABI, ale main.csemantyczny API by się zepsuł .

Istnieją dwa sposoby programowego sprawdzenia interfejsu API umowy:

  • przetestuj kilka skrzynek narożnych. Łatwe do zrobienia, ale zawsze możesz go przegapić.
  • 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.


3

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.


3

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:

  1. Łatka - Nie musisz zmieniać swojego kodu. Biblioteka naprawia tylko kilka błędów.
  2. Drobne - Nie musisz zmieniać kodu od czasu dodania
  3. Major - Interfejs (API) został zmieniony i może być konieczna zmiana kodu.

Aby móc korzystać z nowej wersji głównej tej samej biblioteki, należy przestrzegać wielu innych konwencji:

  • Język binarny używany w bibliotekach (w przypadkach Java wersja docelowa JVM, która definiuje kod bajtowy Java)
  • Konwencje zwoływania
  • Konwencje JVM
  • Konwencje łączenia
  • Konwencje środowiska wykonawczego Wszystkie te są definiowane i zarządzane przez narzędzia, których używamy.

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.jari joda-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 ( -targetopcja 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:

  1. Wersje semantyczne: gdy biblioteki są ukierunkowane na wyższą maszynę JVM, zwykle zmieniają wersję główną.
  2. Użyj JVM 4 jako ABI i jesteś bezpieczny.
  3. Java 9 dodaje specyfikację dotyczącą sposobu włączenia kodu bajtowego dla określonej docelowej maszyny JVM w tej samej bibliotece.

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.


1

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.


1

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):

  1. Taka sama / kompatybilna wstecz architektura zestawu instrukcji (instrukcje procesora, struktura pliku rejestru, organizacja stosu, typy dostępu do pamięci, wraz z rozmiarami, układem i wyrównaniem podstawowych typów danych, do których procesor może bezpośrednio uzyskać dostęp)
  2. Te same konwencje wywoływania
  3. Ta sama konwencja manglingu nazw (może być potrzebna, jeśli powiedzmy, że program Fortran musi wywołać funkcję biblioteki C ++).

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ć:

Funkcjonalność ABI: zgodność binarna

istniejące podmioty: istniejący program / biblioteki / system operacyjny

konsument: biblioteki, system operacyjny

Mam nadzieję że to pomoże!


1

Interfejs binarny aplikacji (ABI)

Funkcjonalność:

  • Tłumaczenie z modelu programisty na typ danych domeny systemu, rozmiar, wyrównanie, konwencję 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; schemat zarządzania nazwami kompilatorów języka wysokiego poziomu, propagacja wyjątków i konwencja wywoływania między kompilatorami na tej samej platformie, ale nie wymagają one zgodności między platformami ...

Istniejące podmioty:

  • Bloki logiczne, które bezpośrednio uczestniczą w wykonywaniu programu: ALU, rejestry ogólnego przeznaczenia, rejestry do mapowania pamięci / we / wy we / wy itp.

konsument:

  • Łącznik procesorów językowych, asembler ...

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.


1

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.

void initfoo(FOO * foo)
int usefoo(FOO * foo, int bar)
void cleanupfoo(FOO * foo)

a programista aplikacji pisze kod podobny do

int dostuffwithfoo(int bar) {
  FOO foo;
  initfoo(&foo);
  int result = usefoo(&foo,bar)
  cleanupfoo(&foo);
  return result;
}

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.

FOO * newfoo(void)
int usefoo(FOO * foo, int bar)
void deletefoo((FOO * foo, int bar))

a programista aplikacji pisze kod podobny do

int dostuffwithfoo(int bar) {
  FOO * foo;
  foo = newfoo();
  int result = usefoo(foo,bar)
  deletefoo(foo);
  return result;
}

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.


0

ABI- Application Binary Interfacedotyczy komunikacji kodu maszynowego w czasie wykonywania między dwiema częściami programu binarnego, takimi jak - aplikacja, biblioteka, system operacyjny ... ABIopisuje 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ą Swifti Objective-C[Mixing Swift i Objective-C]

  • Application - OS- środowisko wykonawcze - Swift runtimei standard librariessą częścią systemu operacyjnego i nie powinny być dołączane do każdego pakietu (np. aplikacji, frameworka). Jest taki sam jak w przypadku Objective-C

  • Library- Module Stabilityprzypadek - 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 .swiftinterfaceużywany przez inną wersję kompilatora ( używanego z .swiftmodule) i nie otrzymasz

    Module compiled with _ cannot be imported by the _ compiler
    
  • Library- Library Evolutionskrzynia

    1. Czas kompilacji - w przypadku zmiany zależności klient nie musi być ponownie kompilowany.
    2. Runtime - biblioteka systemowa lub dynamiczna struktura może być wymieniana podczas pracy przez nową.

[API vs ABI]

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.