Użycie stosu x64
Cała pamięć poza bieżącym adresem dostawcy zasobów jest uważana za nietrwałą: system operacyjny lub debuger może zastąpić tę pamięć podczas sesji debugowania użytkownika lub program obsługi przerwań. W związku z tym przed próbą odczytu lub zapisu wartości w ramce stosu należy zawsze ustawiać dostawcę RSP.
W tej sekcji omówiono alokację przestrzeni stosu dla zmiennych lokalnych i wewnętrznie alloca .
Alokacja stosu
Prolog funkcji jest odpowiedzialny za przydzielanie miejsca stosu dla zmiennych lokalnych, zapisanych rejestrów, parametrów stosu i rejestrowania parametrów.
Obszar parametru jest zawsze w dolnej części stosu (nawet jeśli alloca
jest używany), dzięki czemu zawsze będzie sąsiadować z adresem zwrotnym podczas dowolnego wywołania funkcji. Zawiera co najmniej cztery wpisy, ale zawsze wystarczająco dużo miejsca, aby przechowywać wszystkie parametry wymagane przez dowolną funkcję, która może być wywoływana. Należy pamiętać, że miejsce jest zawsze przydzielane dla parametrów rejestru, nawet jeśli same parametry nigdy nie znajdują się w stosie; obiekt wywoływany gwarantuje, że miejsce zostało przydzielone dla wszystkich jego parametrów. Adresy główne są wymagane dla argumentów rejestru, więc ciągły obszar jest dostępny w przypadku, gdy wywołana funkcja musi podjąć adres listy argumentów (va_list) lub pojedynczy argument. Ten obszar zapewnia również wygodne miejsce do zapisywania argumentów rejestru podczas wykonywania thunk i jako opcji debugowania (na przykład argumenty ułatwiają znalezienie argumentów podczas debugowania, jeśli są przechowywane na ich adresach domowych w kodzie prologu). Nawet jeśli wywołana funkcja ma mniej niż 4 parametry, te 4 lokalizacje stosu są skutecznie własnością wywoływanej funkcji i mogą być używane przez wywoływaną funkcję do innych celów oprócz zapisywania wartości rejestru parametrów. W związku z tym obiekt wywołujący może nie zapisywać informacji w tym regionie stosu w wywołaniu funkcji.
Jeśli spacja jest dynamicznie przydzielana (alloca
) w funkcji, rejestr niewolaty musi być używany jako wskaźnik ramki do oznaczania podstawy stałej części stosu i że rejestr musi zostać zapisany i zainicjowany w prologu. Należy pamiętać, że w przypadku alloca
użycia wywołania do tego samego wywoływania z tego samego obiektu wywołującego mogą mieć różne adresy główne dla ich parametrów rejestru.
Stos będzie zawsze utrzymywany 16 bajtów wyrównany, z wyjątkiem w prologu (na przykład po wypchnięciu adresu zwrotnego) i z wyjątkiem przypadków, w których określono w typach funkcji dla określonej klasy funkcji ramek.
Poniżej przedstawiono przykład układu stosu, w którym funkcja A wywołuje funkcję nielistą B. Prolog funkcji A już przydzielono miejsce dla wszystkich parametrów rejestru i stosu wymaganych przez B w dolnej części stosu. Wywołanie wypycha adres zwrotny, a prolog B przydziela miejsce dla zmiennych lokalnych, rejestrów nieuwolnych i miejsca potrzebnego do wywoływania funkcji. Jeśli usługa B używa alloca
, miejsce jest przydzielane między zmienną lokalną/niewolacyjną rejestracją obszaru zapisywania i obszarem stosu parametrów.
Gdy funkcja B wywołuje inną funkcję, adres zwrotny jest wypychany tuż poniżej adresu głównego RCX.
Dynamiczna konstrukcja obszaru stosu parametrów
Jeśli jest używany wskaźnik ramki, opcja istnieje, aby dynamicznie utworzyć obszar stosu parametrów. Nie jest to obecnie wykonywane w kompilatorze x64.
Typy funkcji
Istnieją w zasadzie dwa typy funkcji. Funkcja, która wymaga ramki stosu, jest nazywana funkcją ramki. Funkcja, która nie wymaga ramki stosu, jest nazywana funkcją liścia.
Funkcja ramki to funkcja, która przydziela przestrzeń stosu, wywołuje inne funkcje, zapisuje niewolne rejestry lub używa obsługi wyjątków. Wymaga również wpisu tabeli funkcji. Funkcja ramki wymaga prologu i epilogu. Funkcja ramki może dynamicznie przydzielać miejsce stosu i stosować wskaźnik ramki. Funkcja ramowa ma pełne możliwości tego standardu wywołującego.
Jeśli funkcja ramki nie wywołuje innej funkcji, nie jest wymagana do wyrównania stosu (przywoływany w sekcji Alokacja stosu).
Funkcja liścia jest funkcją, która nie wymaga wpisu tabeli funkcji. Nie może wprowadzać zmian w żadnych rejestrach niezauwolnych, w tym RSP, co oznacza, że nie może wywoływać żadnych funkcji ani przydzielać miejsca na stosie. Może pozostawić stos nieprzygotowany podczas jego wykonywania.
wyrównanie malloc
Malloc ma gwarancję zwracania pamięci, która jest odpowiednio wyrównana do przechowywania dowolnego obiektu, który ma podstawowe wyrównanie i może zmieścić się w ilości przydzielonej pamięci. Podstawowe wyrównanie to wyrównanie mniejsze lub równe największemu wyrównaniu obsługiwanemu przez implementację bez specyfikacji wyrównania. (W języku Visual C++jest to wyrównanie wymagane dla double
wartości lub 8 bajtów. W kodzie przeznaczonym dla platform 64-bitowych jest to 16 bajtów). Na przykład alokacja czterech bajtów zostanie wyrównana do granicy, która obsługuje dowolny obiekt cztero bajtowy lub mniejszy.
Visual C++ zezwala na typy, które mają rozszerzone wyrównanie, nazywane również typami wyrównaniami nadmiernymi. Na przykład typy SSE __m128 i __m256
i , i, które są deklarowane przy użyciu __declspec(align( n ))
wartości gdzie n
jest większe niż 8, mają rozszerzone wyrównanie. Wyrównanie pamięci na granicy, która jest odpowiednia dla obiektu wymagającego rozszerzonego wyrównania, nie jest gwarantowane przez element malloc
. Aby przydzielić pamięć dla nadmiernie wyrównanych typów, użyj _aligned_malloc i powiązanych funkcji.
alloca
_alloca musi być wyrównana do 16 bajtów i dodatkowo wymagana do użycia wskaźnika ramki.
Przydzielony stos musi zawierać miejsce po nim dla parametrów kolejnych nazywanych funkcjami, zgodnie z opisem w temacie Alokacja stosu.