2016-07-25 55 views
10

Konwencja wywoływania funkcji x86_64 SysV ABI definiuje liczbę całkowitą 4, która ma być przekazana do rejestru rcx. Z drugiej strony, system ABI systemu Linux używa r10 w tym samym celu. Wszystkie pozostałe argumenty są przekazywane w tych samych rejestrach zarówno dla funkcji, jak i dla syscalls.Różnica w ABI między funkcjami Linuksa x86_64 a syscalls

Prowadzi to do dziwnych rzeczy. Sprawdź, na przykład, realizacja mmap w glibc dla platformy x32 (dla których istnieje rozbieżność taka sama):

00432ce0 <__mmap>: 
    432ce0:  49 89 ca    mov %rcx,%r10 
    432ce3:  b8 09 00 00 40   mov $0x40000009,%eax 
    432ce8:  0f 05     syscall 

Więc rejestr są już na swoim miejscu, z wyjątkiem ruszamy rcx do r10.

Zastanawiam się, dlaczego nie zdefiniować ABI systemu operacyjnego tak, aby było takie samo jak wywołanie funkcji ABI, biorąc pod uwagę, że są one już tak podobne.

+0

W [kolejnej odpowiedzi ABI] (http://stackoverflow.com/a/35619528/224132), wykopałem kilka linków do list dyskusyjnych AMD64 od architektów AMD i deweloperów jądra Linux przed wydaniem pierwszego silicon AMD64 . Jest tam kilka ciekawych rzeczy, takich jak wyniki eksperymentalne (od kompilacji SPECinta i przeglądanie rozmiaru kodu i liczby instrukcji), które doprowadziły do ​​wyborów SysV ABI dla x86-64, do których rejestru użyć. –

Odpowiedz

5

Celem jest zapewnienie szybszej metody wprowadzania Ring-0 w celu wykonania wywołania systemowego. Ma to być ulepszenie w stosunku do starej metody, która polegała na wywołaniu przerwania oprogramowania (int 0x80 w systemie Linux).

Jednym z powodów, dla których instrukcja jest szybsza, jest to, że nie zmienia ona pamięci, ani nawet nie zmienia rsp, aby wskazywać na stos jądra. W przeciwieństwie do przerwania programowego, w którym procesor jest zmuszony do umożliwienia systemowi operacyjnemu wznowienia działania bez wciskania czegokolwiek, dla tego polecenia CPU może przyjąć, że oprogramowanie wie, że coś tu się dzieje.

W szczególności syscall przechowuje dwie części stanu przestrzeni użytkownika w rejestrach. RIP do powrotu po wywołaniu jest przechowywany w rcx, a flagi są przechowywane w R11 (because RFLAGS is masked with a kernel-supplied value before entry to the kernel). Oznacza to, że oba te rejestry są przeplatane instrukcją.

Ponieważ są one clobbered, ABI syscall używa innego rejestru zamiast rcx, stąd użycie r10 dla czwartego argumentu.

r10 jest naturalnym wyborem, ponieważ in the x86-64 SystemV ABI nie jest wykorzystywany do przekazywania args funkcji, a funkcje nie trzeba zachować wartość swojego rozmówcy z r10. Tak więc funkcja opakowania syscall może mov %rcx, %r10 bez żadnego zapisu/przywrócenia. Nie byłoby to możliwe z żadnym innym rejestrem, dla systemów 6-arg i konwencji wywoływania funkcji SysV ABI.


BTW, zadzwoń system 32-bitowy ABI jest również dostępny z sysenter, który wymaga współpracy pomiędzy przestrzeni użytkownika i przestrzeni jądra, aby umożliwić powrót do przestrzeni użytkownika po sysenter. (tj. przechowywanie pewnego stanu w przestrzeni użytkownika przed uruchomieniem sysenter). Jest to wyższa wydajność niż int 0x80, ale jest niezręczna. Wciąż używa go glibc (przeskakując do kodu przestrzeni użytkownika na stronach vdso, które jądro zamienia w przestrzeń adresową każdego procesu).

AMD to syscall jest kolejnym podejściem do tego samego pomysłu, co wersja sysenter firmy Intela: zmniejszanie kosztów wejścia/wyjścia z jądra przez nieutrzymywanie absolutnie wszystkiego.

+0

To bardziej subtelne niż zastąpienie kilku sklepów ruchami rejestru rejestru. Nie zmienia on 'rsp', aby wskazywał na stos jądra, więc nie byłoby rozsądnego miejsca, aby pchnąć wszystko, co chciał zapisać. Kod jądra w punkcie wejścia musi to zrobić sam. (użycie 'swapgs' włącza' [gs: absolute_address] ', aby uzyskać dostęp do danych jądra dla każdego zadania). Procesor nie przechowuje również wewnętrznie wskaźnika stosu jądra do użycia dla 'syscall', tylko zapisanej wartości' gs'. Myślę, że właśnie stąd bierze się redukcja stopnia złożoności implementacji. (I że 'swapgs' jest oddzielną instrukcją). –

+0

Część o C/C++ nie używająca 'r10' jest bez znaczenia. Jądro nie może przyjąć, który język wykonuje połączenie. –

+0

W porządku. Edytowałem to. –

4

AMD's syscall clobbers rejestru rcx, w ten sposób używany jest r10.

+1

I 'r10' jest czystym rejestrem scratch: nie jest używane do przekazywania funkcji i nie jest zachowywane wywołanie. Dzięki temu inne funkcje opakowania, takie jak kody pośredniczące w dynamicznym łączu, używają go jako tymczasowego i nadal mogą wywoływać ogony za pomocą 'jmp' zamiast' call'/'pop' /' ret'. Więc 'r10' jest dobrym wyborem dla systemów syscalls. 'syscall' /' sysret' również używają r11. –

+0

Ups, myślałem o 'r11'. ABI mówi, że 'r10' służy do przekazywania" statycznego wskaźnika łańcucha ". C/C++ tego nie używa, więc w praktyce 'r10' jest także czystym rejestrem zdrapek. –