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 nastavena sada RSP.
Tato část popisuje přidělení prostoru zásobníku pro místní proměnné a alokaci vnitřní.
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 nejsou nikdy domovem zásobníku; volaný 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 umístění zásobníku jsou vlastněna volanou funkcí a mohou být používány volanou funkcí pro jiné účely kromě ukládání hodnot registru parametrů. Volající tedy nemusí ukládat informace v této oblasti zásobníku napříč voláním 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 alloca
použití mohou volání stejného volajícího mít pro své parametry registru různé domovské adresy.
Zásobník bude vždy zarovnaný 16 bajtů s výjimkou prologu (například po vložení zpáteční adresy) a s výjimkou případů uvedených v typech funkcí pro určitou třídu funkcí rámce.
Následuje příklad rozložení zásobníku, ve kterém funkce A volá neležovou funkci B. Prolog funkce A už má přidělený prostor pro všechny parametry registru a zásobníku vyžadované B v dolní čá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 používáte alloca
B, je mezera přidělena mezi místní proměnnou nebo nevolatilní oblastí uložení registru a oblastí zásobníku parametrů.
Když funkce B volá jinou funkci, vrátí se zpětná adresa těsně pod domovskou adresou 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 funkce 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 alignment
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 Visual C++ je to zarovnání, které je vyžadováno pro double
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řbajtů nebo menší objekt.
Jazyk Visual C++ povoluje typy s rozšířeným zarovnáním, které se označují také jako typy zarovnané nad zarovnáním. Například typy SSE __m128 a __m256
a typy deklarované pomocí __declspec(align( n ))
n
místa, kde 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.
Sada, která je přidělena, musí za sebou obsahovat prostor pro parametry následně volaných funkcí, jak je popsáno v tématu Přidělení zásobníku.