GNU/Linux >> Znalost Linux >  >> Linux

Jaké jsou konvence volání pro systémová volání UNIX a Linux (a funkce v uživatelském prostoru) na i386 a x86-64

Další informace o kterémkoli z témat naleznete zde:Definitivní průvodce systémovými voláními systému Linux

Ověřil jsem je pomocí GNU Assembler (plyn) na Linuxu.

Rozhraní jádra

Konvence systémového volání x86-32 aka i386 Linux:

V x86-32 jsou parametry pro systémová volání Linux předávány pomocí registrů. %eax pro systémové_číslo. %ebx, %ecx, %edx, %esi, %edi, %ebp se používají pro předání 6 parametrů do systémových volání.

Vrácená hodnota je v %eax . Všechny ostatní registry (včetně EFLAGS) jsou zachovány v int $0x80 .

Vzal jsem si následující úryvek z Linux Assembly Tutorial, ale pochybuji o tom. Pokud někdo může ukázat příklad, bylo by to skvělé.

Pokud existuje více než šest argumentů, %ebx musí obsahovat paměťové umístění, kde je uložen seznam argumentů – ale s tím si nedělejte starosti, protože je nepravděpodobné, že byste použili systémové volání s více než šesti argumenty.

Příklad a trochu více informací naleznete na http://www.int80h.org/bsdasm/#alternate-calling-convention. Další příklad Hello World pro i386 Linux pomocí int 0x80 :Dobrý den, svět v assembleru se systémovými voláními Linuxu?

Existuje rychlejší způsob, jak provádět 32bitová systémová volání:pomocí sysenter . Jádro namapuje stránku paměti do každého procesu (vDSO) se stranou uživatelského prostoru sysenter dance, který musí spolupracovat s jádrem, aby mohl najít zpáteční adresu. Mapování Arg pro registraci je stejné jako pro int $0x80 . Místo použití sysenter byste měli normálně volat do vDSO přímo. (Viz Definitivní průvodce systémovými voláními systému Linux pro informace o propojení a volání do vDSO a pro další informace o sysenter a vše ostatní, co se týká systémových volání.)

x86-32 [Free|Open|Net|DragonFly]Konvence systémového volání BSD UNIX:

Parametry se předávají na zásobníku. Přesuňte parametry (poslední parametr posunutý jako první) do zásobníku. Poté vložte dalších 32bitových fiktivních dat (nejsou to ve skutečnosti fiktivní data. Další informace naleznete na následujícím odkazu) a poté zadejte instrukci systémového volání int $0x80

http://www.int80h.org/bsdasm/#default-calling-convention

Konvence systémového volání x86-64 Linux:

(Poznámka:x86-64 Mac OS X je podobný, ale odlišný od Linuxu. TODO:zkontrolujte, co dělá *BSD)

Viz část:"A.2 AMD64 Linux Konvence jádra" systému V Application Binary Interface AMD64 Architecture Processor Supplement. Nejnovější verze i386 a x86-64 System V psABI lze nalézt odkazem z této stránky v repozitáři správce ABI. (Viz také x86 tag wiki pro up- dosavadní odkazy na ABI a spoustu dalších dobrých věcí o x86 asm.)

Zde je úryvek z této sekce:

  1. Aplikace na uživatelské úrovni používají jako celočíselné registry pro předávání sekvence %rdi, %rsi, %rdx, %rcx,%r8 a %r9. Rozhraní jádra používá %rdi, %rsi, %rdx, %r10, %r8 a %r9.
  2. Systémové volání se provádí prostřednictvím syscall instrukce . Toto clobuje %rcx a %r11 a také návratovou hodnotu %rax, ale ostatní registry jsou zachovány.
  3. Číslo syscall musí být předáno v registru %rax.
  4. Systémová volání jsou omezena na šest argumentů, žádný argument není předán přímo do zásobníku.
  5. Registr %rax po návratu ze systémového volání obsahuje výsledek systémového volání. Hodnota v rozsahu -4095 až -1 označuje chybu, je to -errno .
  6. Jen hodnoty třídy INTEGER nebo třídy MEMORY jsou předány jádru.

Pamatujte, že toto je z přílohy ABI specifické pro Linux a dokonce i pro Linux je informativní, nikoli normativní. (Ale ve skutečnosti je to přesné.)

Tento 32bitový int $0x80 ABI je použitelné v 64bitovém kódu (ale důrazně se nedoporučuje). Co se stane, když použijete 32bitové int 0x80 Linux ABI v 64bitovém kódu? Stále ořezává své vstupy na 32 bitů, takže je nevhodný pro ukazatele a nuluje r8-r11.

Uživatelské rozhraní:volání funkcí

Konvence volání funkcí x86-32:

V x86-32 byly parametry předány do zásobníku. Poslední parametr byl vložen nejprve do zásobníku, dokud nejsou všechny parametry hotové, a poté call instrukce byla provedena. To se používá pro volání funkcí knihovny C (libc) na Linuxu z assembleru.

Moderní verze i386 System V ABI (používané v Linuxu) vyžadují 16bajtové zarovnání %esp před call , jako x86-64 System V ABI vždy vyžadoval. Volaní mohou předpokládat a používat SSE 16bajtové načtení/uložení této chyby na nezarovnané. Historicky však Linux vyžadoval pouze zarovnání 4bajtového zásobníku, takže rezervování přirozeně zarovnaného prostoru i pro 8bajtový double vyžadovalo další práci nebo tak něco.

Některé další moderní 32bitové systémy stále nevyžadují více než 4bajtové zarovnání zásobníku.

Konvence volání funkcí x86-64 System V uživatelského prostoru:

x86-64 System V předává argumenty v registrech, což je efektivnější než konvence zásobníkových argumentů i386 System V. Vyhne se latenci a dalším instrukcím při ukládání argumentů do paměti (mezipaměti) a jejich opětovném načítání zpět do volaného. To funguje dobře, protože je k dispozici více registrů a je to lepší pro moderní vysoce výkonné CPU, kde záleží na latenci a provedení mimo pořadí. (I386 ABI je velmi staré).

V této nové mechanismus:Nejprve jsou parametry rozděleny do tříd. Třída každého parametru určuje způsob, jakým je předán volané funkci.

Úplné informace naleznete v části:"3.2 Function Calling Sequence" dodatku System V Application Binary Interface AMD64 Architecture Processor, který částečně zní:

Jakmile jsou argumenty klasifikovány, registry se přiřadí (pořadí zleva doprava) pro předání následovně:

  1. Pokud je třída MEMORY, předejte argument na zásobníku.
  2. Pokud je třída INTEGER, použije se další dostupný registr sekvence %rdi, %rsi, %rdx, %rcx, %r8 a %r9

Takže %rdi, %rsi, %rdx, %rcx, %r8 and %r9 jsou registry v pořádku používá se k předání parametrů typu celé číslo/ukazatel (tj. třída INTEGER) jakékoli funkci libc z assembleru. %rdi se používá pro první parametr INTEGER. %rsi za 2., %rdx za 3. a tak dále. Potom call měla by být poskytnuta instrukce. Zásobník (%rsp ) musí být zarovnán na 16B, když call provede.

Pokud existuje více než 6 parametrů INTEGER, je do zásobníku předán 7. parametr INTEGER a pozdější. (Volající se objeví, stejně jako x86-32.)

Prvních 8 argumentů s pohyblivou řádovou čárkou je předáno v %xmm0-7, později v zásobníku. Neexistují žádné vektorové registry se zachováním volání. (Funkce s kombinací FP a celočíselných argumentů může mít celkem více než 8 argumentů registru.)

Variadické funkce (jako printf ) vždy potřebujete %al =počet argumentů registru FP.

Existují pravidla pro to, kdy zabalit struktury do registrů (rdx:rax při návratu) vs. v paměti. Podrobnosti najdete v ABI a zkontrolujte výstup kompilátoru, abyste se ujistili, že váš kód souhlasí s kompilátory ohledně toho, jak má být něco předáno/vráceno.

Všimněte si, že konvence volání funkcí systému Windows x64 má několik významných rozdílů od x86-64 System V, jako je stínový prostor, který musí být rezervován volajícím (místo červené zóny) a zakonzervován xmm6-xmm15. A velmi odlišná pravidla pro to, který argument jde do kterého registru.


Možná hledáte x86_64 ABI?

  • www.x86-64.org/documentation/abi.pdf (404 at 2018-11-24)
  • www.x86-64.org/documentation/abi.pdf (prostřednictvím Wayback Machine dne 24. 11. 2018)
  • Kde je zdokumentováno rozhraní x86-64 System V ABI? - https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI je aktualizováno (od HJ Lu, jednoho ze správců ABI) s odkazy na soubory PDF oficiální aktuální verze.

Pokud to není přesně to, co hledáte, použijte 'x86_64 abi' ve svém preferovaném vyhledávači, abyste našli alternativní reference.


Volací konvence definují, jak jsou parametry předávány v registrech při volání nebo volání jiným programem. A nejlepší zdroj těchto konvencí je ve formě standardů ABI definovaných pro každý tento hardware. Pro usnadnění kompilace je stejné ABI také používáno uživatelským prostorem a programem jádra. Linux/Freebsd se řídí stejným ABI pro x86-64 a jinou sadou pro 32-bit. Ale x86-64 ABI pro Windows se liší od Linux/FreeBSD. A obecně ABI nerozlišuje systémové volání od normálních „volání funkcí“. Tj. zde je konkrétní příklad konvencí volání x86_64 a je stejný pro uživatelský prostor Linuxu i jádro:http://eli.thegreenplace.net/2011/ 09/06/stack-frame-layout-on-x86-64/ (všimněte si pořadí parametrů a,b,c,d,e,f):

Výkon je jedním z důvodů těchto ABI (např. předávání parametrů přes registry namísto ukládání do zásobníků paměti)

Pro ARM existují různé 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

Konvence ARM64:

http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf

Pro Linux na PowerPC:

http://refspecs.freestandards.org/elf/elfspec_ppc.pdf

http://www.0x04.net/doc/elf/psABI-ppc64.pdf

A pro embedded existuje PPC EABI:

http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf

Tento dokument je dobrým přehledem všech různých konvencí:

http://www.agner.org/optimize/calling_conventions.pdf


Linux
  1. Linux vs. Unix:Jaký je rozdíl?

  2. Jaká je správná velikost odkládacího prostoru pro moderní systém Linux?

  3. Linux – Jak zjistit, jaké pevné disky jsou v systému?

  1. Jaké je rozhraní pro systémová volání ARM a kde je definováno v jádře Linuxu?

  2. Jaké jsou rozdíly mezi lsof a netstat na linuxu?

  3. Jaké je konvenční umístění instalace aplikací v Linuxu?

  1. Jaké jsou dobré knihy o Linuxu/Unixu pro pokročilé uživatele?

  2. Jaké jsou funkce systému BIOS, když je spuštěn operační systém?

  3. Jaký je rozdíl mezi voláním knihovny a voláním systému v Linuxu?