Poniższe linki wyjaśniają konwencje wywołań systemowych x86-32 zarówno dla UNIX (smak BSD), jak i Linux:
Ale jakie są konwencje wywołań systemowych x86-64 w systemach UNIX i Linux?
Poniższe linki wyjaśniają konwencje wywołań systemowych x86-32 zarówno dla UNIX (smak BSD), jak i Linux:
Ale jakie są konwencje wywołań systemowych x86-64 w systemach UNIX i Linux?
sysret
działania, wraz z zastąpieniem rax wartością zwracaną. Wszystkie inne rejestry są zachowane w amd64.Odpowiedzi:
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 wint $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.
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 dlaint $0x80
. Zwykle powinieneś dzwonić do vDSO zamiast używać gosysenter
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 informacjisysenter
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 systemowego
int $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:
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 acall
, 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-bajtowegodouble
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:
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 jak
printf
) 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.
źródło
int 0x80
ABI Linuksa w 64-bitowym kodzie, dokładnie tak się dzieje: stackoverflow.com/questions/46087730/… . Zeruje r8-r11 i działa dokładnie tak, jak w przypadku uruchomienia w procesie 32-bitowym. W tym pytaniu i odpowiedzi mam przykład pokazujący, że działa lub nie działa z obcięciem wskaźnika. Zagłębiłem się również w źródłach jądra, aby pokazać, dlaczego tak się zachowuje.Być może szukasz interfejsu ABI x86_64?
Jeśli nie jest to dokładnie to, czego szukasz, użyj „x86_64 abi” w preferowanej wyszukiwarce, aby znaleźć alternatywne odniesienia.
źródło
Konwencje wywoływania definiują sposób przekazywania parametrów do rejestrów podczas wywoływania lub wywoływania przez inny program. A najlepszym źródłem tej konwencji są standardy ABI zdefiniowane dla każdego z tych urządzeń. Aby ułatwić kompilację, ten sam interfejs ABI jest również używany przez przestrzeń użytkownika i program jądra. Linux / Freebsd stosuje ten sam ABI dla x86-64 i inny zestaw dla wersji 32-bitowej. Ale x86-64 ABI dla Windows różni się od Linux / FreeBSD. I ogólnie ABI nie rozróżnia wywołań systemowych od zwykłych „wywołań funkcji”. To znaczy, oto szczególny przykład konwencji wywoływania x86_64 i jest taki sam dla przestrzeni użytkownika i jądra Linuksa: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64 / (zwróć uwagę na sekwencję a, b, c, d, e, f parametrów):
Wydajność jest jednym z powodów tych ABI (np. Przekazywanie parametrów przez rejestry zamiast zapisywania w stosach pamięci)
W przypadku ARM istnieją różne ABI:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.swdev.abi/index.html
https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/iPhoneOSABIReference.pdf
Konwencja ARM64:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
Linux na PowerPC:
http://refspecs.freestandards.org/elf/elfspec_ppc.pdf
http://www.0x04.net/doc/elf/psABI-ppc64.pdf
A dla rozwiązań wbudowanych jest PPC EABI:
http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf
Ten dokument zawiera dobry przegląd wszystkich różnych konwencji:
http://www.agner.org/optimize/calling_conventions.pdf
źródło
Komentarze do źródeł jądra Linux 5.0
Wiedziałem, że specyfika x86 jest za
arch/x86
słaba, a rzeczy syscall się nie sprawdzająarch/x86/entry
. Tak więc szybkie przejściegit grep rdi
do tego katalogu prowadzi mnie do arch / x86 / entry / entry_64.S :i dla wersji 32-bitowej w arch / x86 / entry / entry_32.S :
glibc 2.29 Linux x86_64 implementacja wywołania systemowego
Teraz oszukujmy, patrząc na główne implementacje libc i zobaczmy, co robią.
Co może być lepszego niż zajrzenie do glibc, którego używam teraz, pisząc tę odpowiedź? :-)
glibc 2.29 definiuje wywołania systemowe x86_64 w
sysdeps/unix/sysv/linux/x86_64/sysdep.h
i zawiera interesujący kod, np .:i:
które moim zdaniem są dość oczywiste. Zwróć uwagę, jak wydaje się, że zostało to zaprojektowane tak, aby dokładnie pasowało do konwencji wywoływania zwykłych funkcji AMD64 ABI systemu V: https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions
Szybkie przypomnienie o napastnikach:
cc
oznacza rejestry flag. Ale Peter Cordes komentuje że nie jest to tutaj konieczne.memory
oznacza, że wskaźnik może być przekazywany w asemblerze i używany do uzyskiwania dostępu do pamięciAby uzyskać wyraźny, minimalny, uruchamialny przykład od podstaw, zobacz odpowiedź: Jak wywołać wywołanie systemowe za pośrednictwem sysenter w asemblerze wbudowanym?
Wykonaj ręcznie wywołania systemowe w asemblerze
Niezbyt naukowe, ale zabawne:
x86_64.S
GitHub upstream .
aarch64
Pokazałem minimalny działający przykład przestrzeni użytkownika pod adresem : /reverseengineering/16917/arm64-syscalls-table/18834#18834 TODO kod jądra grep tutaj, powinien być łatwy.
źródło
"cc"
Clobber jest niepotrzebna: syscalli Linux zapisu / przywrócenia RFLAGS (Thesyscall
/sysret
instrukcje zrobić za pomocą R11, a jądro nie zmienia zapisany R11 / RFLAGS inny niż poprzezptrace
debugera wywołań systemowych.) Nie, że kiedykolwiek się liczy, bo"cc"
clobber jest domyślnie dla x86 / x86-64 w GNU C Extended asm, więc nie możesz nic zyskać, pomijając to.