Dalsze czytanie dowolnego z tematów tutaj: Ostateczny przewodnik po wywołaniach systemu Linux
Sprawdziłem je używając GNU Assembler (gas) w Linuksie.
Interfejs jądra
x86-32 aka i386 Konwencja wywołań systemowych Linux:
W x86-32 parametry dla wywołań systemowych Linuksa są przekazywane za pomocą rejestrów. %eax
dla syscall_number. % ebx,% ecx,% edx,% esi,% edi,% ebp służą do przekazywania 6 parametrów do wywołań systemowych.
Wartość zwracana jest w %eax
. Wszystkie inne rejestry (w tym EFLAGS) są zachowywane w int $0x80
.
Wziąłem następujący fragment z samouczka montażu Linuksa, ale mam co do tego wątpliwości. Byłoby świetnie, gdyby ktoś mógł pokazać przykład.
Jeśli jest więcej niż sześć argumentów,
%ebx
musi zawierać lokalizację pamięci, w której przechowywana jest lista argumentów - ale nie martw się tym, ponieważ jest mało prawdopodobne, że użyjesz wywołania systemowego z więcej niż sześcioma argumentami.
Przykład i trochę więcej informacji można znaleźć pod adresem http://www.int80h.org/bsdasm/#alternate-calling-convention . Inny przykład Hello World dla i386 Linux używający int 0x80
: Hello, world in asembler z wywołaniami systemu Linux?
Istnieje szybszy sposób wykonywania 32-bitowych wywołań systemowych: użycie sysenter
. Jądro mapuje stronę pamięci do każdego procesu (vDSO), ze stroną sysenter
tańca przestrzeni użytkownika , która musi współpracować z jądrem, aby móc znaleźć adres zwrotny. Argument mapowania rejestrów jest taki sam jak dla int $0x80
. Zwykle powinieneś dzwonić do vDSO zamiast używać go sysenter
bezpośrednio. (Zobacz The Definitive Guide to Linux System Calls, aby uzyskać informacje na temat łączenia i wywoływania vDSO, a także więcej informacji sysenter
i wszystkiego, co ma związek z wywołaniami systemowymi).
x86-32 [Free | Open | Net | DragonFly] Konwencja wywołań systemowych BSD UNIX:
Parametry są przekazywane na stosie. Umieść parametry (ostatni parametr wstawiony jako pierwszy) na stos. Następnie wypchnij dodatkowe 32-bitowe fikcyjne dane (nie są to faktycznie fikcyjne dane. Więcej informacji znajdziesz w poniższym linku), a następnie podaj instrukcję wywołania systemowegoint $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
Konwencja wywołań systemowych Linux x86-64:
x86-64 Mac OS X jest podobny, ale inny . DO ZROBIENIA: sprawdź, co robi * BSD.
Zobacz sekcję: „A.2 Konwencje jądra systemu AMD64 Linux ” w Dodatku do interfejsu binarnego aplikacji Systemu V Dodatek do procesora architektury AMD64 . Można znaleźć najnowsze wersje psABI Systemu V i386 i x86-64 na tej stronie w repozytorium opiekuna ABI . (Zobacz takżex86 oznacz wiki dla aktualnych linków ABI i wielu innych dobrych rzeczy o asm x86.)
Oto fragment z tej sekcji:
- Aplikacje na poziomie użytkownika używają rejestrów całkowitych do przekazywania sekwencji% rdi,% rsi,% rdx,% rcx,% r8 i% r9. Interfejs jądra używa% rdi,% rsi,% rdx,% r10,% r8 i% r9.
- Wywołanie systemowe odbywa się za pośrednictwem
syscall
instrukcji . Powoduje to przebicie% rcx i% r11, a także wartości zwracanej% rax, ale inne rejestry są zachowane.
- Numer wywołania systemowego musi zostać przekazany do rejestru% rax.
- Wywołania systemowe są ograniczone do sześciu argumentów, żaden argument nie jest przekazywany bezpośrednio na stos.
- Wracając z wywołania systemowego, rejestr% rax zawiera wynik wywołania systemowego. Wartość w zakresie od -4095 do -1 wskazuje na błąd
-errno
.
- Do jądra przekazywane są tylko wartości klasy INTEGER lub MEMORY.
Pamiętaj, że pochodzi to z dodatku do ABI specyficznego dla Linuksa, a nawet dla Linuksa ma charakter informacyjny, a nie normatywny. (Ale w rzeczywistości jest dokładny.)
Ten 32-bitowy int $0x80
ABI jest użyteczny w 64-bitowym kodzie (ale zdecydowanie nie jest zalecany). Co się stanie, jeśli użyjesz 32-bitowego int 0x80 Linux ABI w kodzie 64-bitowym? Nadal skraca swoje dane wejściowe do 32-bitowych, więc nie nadaje się do wskaźników i zeruje r8-r11.
Interfejs użytkownika: wywołanie funkcji
Konwencja wywoływania funkcji x86-32:
W x86-32 parametry były przekazywane na stosie. Ostatni parametr został najpierw odłożony na stos, aż wszystkie parametry zostały wykonane, a następnie call
została wykonana instrukcja. Służy do wywoływania funkcji biblioteki C (libc) w systemie Linux z zestawu.
Nowoczesne wersje i386 System V ABI (używane w Linuksie) wymagają 16-bajtowego wyrównania %esp
przed a call
, tak jak w przypadku x86-64 System V ABI. Osoby wywołujące mogą założyć, że i używać 16-bajtowych obciążeń / magazynów SSE, które powodują błędy w stanie niewyrównanym. Ale historycznie Linux wymagał tylko 4-bajtowego wyrównania stosu, więc zarezerwowanie naturalnie wyrównanego miejsca wymagało dodatkowej pracy, nawet dla 8-bajtowego double
lub czegoś podobnego.
Niektóre inne nowoczesne systemy 32-bitowe nadal nie wymagają wyrównania stosu więcej niż 4 bajty.
x86-64 Konwencja wywoływania funkcji w przestrzeni użytkownika Systemu V:
x86-64 System V przekazuje argumenty do rejestrów, co jest bardziej wydajne niż konwencja argumentów stosu w i386 System V. Pozwala uniknąć opóźnień i dodatkowych instrukcji dotyczących przechowywania argumentów w pamięci (cache), a następnie ponownego ładowania ich do wywoływanego. Działa to dobrze, ponieważ dostępnych jest więcej rejestrów i jest lepsze dla nowoczesnych procesorów o wysokiej wydajności, w których liczy się opóźnienie i wykonanie poza kolejnością. (I386 ABI jest bardzo stary).
W tym nowym mechanizmie: Najpierw parametry są podzielone na klasy. Klasa każdego parametru określa sposób, w jaki jest on przekazywany do wywoływanej funkcji.
Pełne informacje można znaleźć w: „3.2 Sekwencja wywoływania funkcji” w Dodatku binarnym interfejsu aplikacji Systemu V do procesora architektury AMD64 , w którym czytamy między innymi:
Po sklasyfikowaniu argumentów rejestry są przypisywane (w kolejności od lewej do prawej) do przekazania w następujący sposób:
- Jeśli klasa to MEMORY, przekaż argument na stosie.
- Jeśli klasa to INTEGER, używany jest następny dostępny rejestr sekwencji% rdi,% rsi,% rdx,% rcx,% r8 i% r9
Kolejność%rdi, %rsi, %rdx, %rcx, %r8 and %r9
rejestrów jest więc używana do przekazywania parametrów całkowitoliczbowych / wskaźników (tj. Klasy INTEGER) do dowolnej funkcji libc z assemblera. % rdi jest używany jako pierwszy parametr INTEGER. % rsi dla drugiego,% rdx dla trzeciego i tak dalej. Następnie należy udzielić instrukcji. Metoda stack ( ) musi być wyrównana do 16B podczas wykonywania.call
%rsp
call
Jeśli jest więcej niż 6 parametrów INTEGER, na stos przekazywany jest siódmy parametr INTEGER i później. (Wyskakuje dzwoniący, tak samo jak x86-32.)
Pierwsze 8 argumentów zmiennoprzecinkowych jest przekazywanych w% xmm0-7, później na stosie. Nie ma rejestrów wektorów z zachowanymi połączeniami. (Funkcja z kombinacją argumentów FP i liczb całkowitych może mieć łącznie więcej niż 8 argumentów rejestru).
Funkcje zmienne ( takie jakprintf
) zawsze potrzebują %al
= liczba argumentów rejestru FP.
Istnieją zasady dotyczące tego, kiedy pakować struktury do rejestrów ( rdx:rax
po powrocie), a kiedy w pamięci. Zobacz ABI, aby uzyskać szczegółowe informacje, i sprawdź dane wyjściowe kompilatora, aby upewnić się, że kod zgadza się z kompilatorami, w jaki sposób coś powinno zostać przekazane / zwrócone.
Zauważ, że konwencja wywoływania funkcji Windows x64 ma wiele znaczących różnic w stosunku do x86-64 System V, na przykład przestrzeń w cieniu, która musi być zarezerwowana przez wywołującego (zamiast czerwonej strefy) i zachowana rozmowa xmm6-xmm15. I bardzo różne reguły, dla których argument trafia do którego rejestru.