Sdílet prostřednictvím


x64 – použití zásobníku

Veškerá paměť nad rámec aktuální adresy RSP je považována za nestálou: Operační systém nebo ladicí program může tuto paměť přepsat během relace ladění uživatele nebo obslužné rutiny přerušení. Proto musí být před pokusem o čtení nebo zápisu hodnot do rámce zásobníku vždy nastaven registr RSP.

Tato část popisuje přidělení prostoru zásobníku pro místní proměnné a alloca intrinsic.

Přidělení zásobníku

Prolog funkce zodpovídá za přidělování prostoru zásobníku pro místní proměnné, uložené registry, parametry zásobníku a parametry registru.

Oblast parametrů je vždy v dolní části zásobníku (i když alloca se používá), takže bude vždy sousedit s návratovou adresou během jakéhokoli volání funkce. Obsahuje alespoň čtyři položky, ale vždy dostatek místa pro uložení všech parametrů potřebných libovolnou funkcí, která může být volána. Všimněte si, že prostor je vždy přidělen pro parametry registru, i když samotné parametry nikdy nejsou uloženy do zásobníku; volanému je zaručeno, že prostor byl přidělen pro všechny jeho parametry. Pro argumenty registru se vyžadují domovské adresy, aby byla k dispozici souvislá oblast pro případ, že by volaná funkce potřebovala získat adresu seznamu argumentů (va_list) nebo jednotlivého argumentu. Tato oblast také poskytuje vhodné místo pro uložení argumentů registru během provádění thunk a jako možnost ladění (například umožňuje snadno najít argumenty během ladění, pokud jsou uloženy na jejich domovské adrese v kódu prologu). I když má volaná funkce méně než 4 parametry, tyto 4 pozice v zásobníku patří volané funkci a mohou být využity pro jiné účely než jen pro uložení hodnoty registrů parametrů. Volající tedy nesmí ukládat informace v této oblasti zásobníku během volání funkce.

Pokud je prostor dynamicky přidělován (alloca) ve funkci, musí být jako ukazatel rámce použit nevolatilní registr, který označuje základ pevné části zásobníku a tento registr musí být uložen a inicializován v prologu. Všimněte si, že při použití alloca mohou volání stejného volajícího na stejný volaný mít pro své parametry registru různé domovské adresy.

Zásobník bude vždy udržován zarovnaný na 16 bajtů, s výjimkou prologu (například po vložení adresy návratu) a s výjimkou případů uvedených v typech funkcí pro určitou třídu rámcových funkcí.

Následuje příklad rozložení zásobníku, kde funkce A volá bezlistovou funkci B. Úvodní kód (prolog) funkce A už má přidělený prostor pro všechny parametry registrů a zásobníkové parametry vyžadované B na spodní části zásobníku. Volání nasdílí zpáteční adresu a prolog B přidělí místo pro místní proměnné, nevolatilní registry a prostor potřebný k volání funkcí. Pokud B používá alloca, je paměť přidělena mezi úložištěm místních proměnných/nevolatilního registru a zásobníkovou oblastí parametrů.

Diagram rozložení zásobníku pro příklad převodu x64

Když funkce B volá jinou funkci, vratná adresa je uložena těsně pod výchozí adresou pro RCX.

Konstrukce oblasti dynamického zásobníku parametrů

Pokud se použije ukazatel rámce, existuje možnost dynamického vytvoření oblasti zásobníku parametrů. V kompilátoru x64 se to v současné době neprokončuje.

Typy funkcí

V podstatě existují dva typy funkcí. Funkce, která vyžaduje rám zásobníku , se nazývá funkce rámce. Funkce, která nevyžaduje rám zásobníku, se nazývá listová funkce.

Funkce rámce je funkce, která přiděluje prostor zásobníku, volá jiné funkce, ukládá nevolatilní registry nebo používá zpracování výjimek. Vyžaduje také položku tabulky funkcí. Funkce rámce vyžaduje prolog a epilog. Funkce rámce může dynamicky přidělovat prostor zásobníku a může použít ukazatel rámce. Funkce rámce má k dispozici všechny možnosti tohoto volacího standardu.

Pokud funkce rámce nevolá jinou funkci, není nutné zarovnat zásobník (odkazované v části Přidělení zásobníku).

Listová funkce je funkce, která nevyžaduje položku tabulky funkcí. Nemůže provádět změny žádných nevolatilních registrů, včetně RSP, což znamená, že nemůže volat žádné funkce ani přidělit prostor zásobníku. Během provádění je povoleno nechat zásobník nerovnaný.

malloc zarovnání

malloc je zaručeno, že vrátí paměť, která je vhodně zarovnaná pro uložení libovolného objektu, který má základní zarovnání a který by se vešl do množství přidělené paměti. Základní zarovnání je zarovnání, které je menší než nebo rovno největší zarovnání podporované implementací bez specifikace zarovnání. (V jazyce Microsoft C++ je to zarovnání, které je vyžadováno pro double nebo 8 bajtů. V kódu, který cílí na 64bitové platformy, je to 16 bajtů.) Například přidělení čtyř bajtů by bylo zarovnané na hranici, která podporuje libovolný čtyřbajtový nebo menší objekt.

Jazyk Visual C++ umožňuje typy s rozšířeným zarovnáním, které se také nazývají přezarovnané typy. Například typy SSE __m128 a __m256, stejně jako typy deklarované s použitím __declspec(align( n )), kde n je větší než 8, mají rozšířené zarovnání. Zarovnání paměti na hranici vhodné pro objekt, který vyžaduje rozšířené zarovnání, není zaručeno malloc. Pokud chcete přidělit paměť pro přerovnané typy, použijte _aligned_malloc a související funkce.

alloca

_alloca musí být zarovnaný na 16 bajtů a navíc je nutné použít ukazatel rámce.

Zásobník, který je přidělen, musí za sebou obsahovat prostor pro parametry následně volaných funkcí, jak je popsáno v Přidělení zásobníku.

Viz také

x64 – softwarové konvence
zarovnat
__declspec