Czym dokładnie jest wskaźnik bazowy i wskaźnik stosu? Do czego zmierzają?

Użycie tego przykładu pochodzącego z Wikipedii, w którym drawsquare wywołuje Drawline (),

alt text

(zauważ, że ten diagram ma wysokie adresy na dole i niskie adresy na górze.)

Czy ktoś mógłby mi wyjaśnić, czym są ebp i esp w tym kontekście?

Z tego co widzę, powiedziałbym, że wskaźnik stosu wskazuje zawsze na górę stosu, a wskaźnik bazowy na początek bieżącej funkcji? Albo co?


Edit: mam na myśli to w kontekście programów windows

Edit2: a jak działa eip?

Edit3: mam następujący kod z MSVC++:

var_C= dword ptr -0Ch
var_8= dword ptr -8
var_4= dword ptr -4
hInstance= dword ptr  8
hPrevInstance= dword ptr  0Ch
lpCmdLine= dword ptr  10h
nShowCmd= dword ptr  14h

Wszystkie zdają się być dwordami, więc każdy zajmuje 4 bajty. WiÄ ™ c wiÄ ™ c wiÄ ™ c jest luka od hInstance do var_4 z 4 bajtăłw. Co to jest? Zakładam, że jest to adres zwrotny, co widać na zdjęciu Wikipedii?


(Uwaga wydawcy: usunięto długi cytat z odpowiedzi Michaela, który nie należy do pytanie, ale kolejne pytanie było edytowane w):

Dzieje się tak dlatego, że przepływ wywołania funkcji wynosi:

* Push parameters (hInstance, etc.)
* Call function, which pushes return address
* Push ebp
* Allocate space for locals

Moje pytanie (Ostatnie, mam nadzieję!) teraz jest, co dokładnie dzieje się od chwili, gdy pop argumenty funkcji, którą chcę wywołać do końca prologu? Chcę wiedzieć, jak ewoluują ebp, esp w tych momentach(już zrozumiałem, jak działa prolog, chcę tylko wiedzieć, co się dzieje po wypchnięciu argumentów na stos i przed prolog).

 193
Author: Community, 2009-09-08

8 answers

esp jest jak mówisz, szczyt stosu.

ebp jest zwykle ustawiona na esp na początku funkcji. Parametry funkcji i zmienne lokalne są dostępne przez dodanie i odejmowanie, odpowiednio, stałego przesunięcia z ebp. Wszystkie konwencje wywołujące x86 definiują ebp jako zachowywane przez wywołania funkcji. ebp w rzeczywistości wskazuje na wskaźnik bazowy poprzedniej ramki, który umożliwia poruszanie się w stosie w debuggerze i przeglądanie lokalnych zmiennych innych ramek.

Większość prologów funkcji wygląda tak:

push ebp      ; Preserve current frame pointer
mov ebp, esp  ; Create new frame pointer pointing to current stack top
sub esp, 20   ; allocate 20 bytes worth of locals on stack.

Później w funkcji możesz mieć kod podobny (zakładając, że obie zmienne lokalne mają 4 bajty)

mov [ebp-4], eax    ; Store eax in first local
mov ebx, [ebp - 8]  ; Load ebx from second local

FPO lub pominięcie wskaźnika ramki optymalizacja, którą możesz włączyć, w rzeczywistości wyeliminuje to i użyje {[4] } jako innego rejestru i dostępu do lokalnych miejsc bezpośrednio poza esp, ale to utrudnia debugowanie, ponieważ debugger nie może już bezpośrednio uzyskać dostępu do ramek stosu wcześniejszych wywołania funkcji.

EDIT:

Dla Twojego zaktualizowanego pytania, brakujące dwa wpisy w stosie to:

var_C = dword ptr -0Ch
var_8 = dword ptr -8
var_4 = dword ptr -4
*savedFramePointer = dword ptr 0*
*return address = dword ptr 4*
hInstance = dword ptr  8h
PrevInstance = dword ptr  0C
hlpCmdLine = dword ptr  10h
nShowCmd = dword ptr  14h

Dzieje się tak dlatego, że przepływ wywołania funkcji wynosi:

  • parametry Push (hInstance, itd.)
  • funkcja wywołania, która wypycha adres zwrotny
  • Push ebp
  • przeznaczyć miejsce dla mieszkańców
 203
Author: Michael,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-10 05:58:22

ESP jest bieżącym wskaźnikiem stosu, który zmienia się za każdym razem, gdy słowo lub adres zostanie wypchnięte lub wyskakujące na/wyłączone ze stosu. EBP jest wygodniejszym sposobem dla kompilatora na śledzenie parametrów funkcji i zmiennych lokalnych niż bezpośrednie korzystanie z ESP.

Ogólnie (i może się to różnić w zależności od kompilatora), wszystkie argumenty wywołanej funkcji są wypychane na stos (zwykle w odwrotnej kolejności, która jest zadeklarowana w prototypie funkcji, ale to różna). Następnie wywoływana jest funkcja, która wypycha adres zwrotny (EIP) na stos.

Po wejściu do funkcji Stara wartość EBP jest wypychana na stos i EBP jest ustawiana na wartość ESP. Następnie ESP jest zmniejszany (ponieważ stos rośnie w dół w pamięci), aby przydzielić miejsce dla zmiennych lokalnych funkcji i tymczasowych. Od tego momentu, podczas wykonywania funkcji, argumenty do funkcji znajdują się na stosie przy dodatnich przesunięciach z EBP (ponieważ zostały wypchnięte przed wywołaniem funkcji), a zmienne lokalne znajdują się przy ujemnych przesunięciach z EBP (ponieważ zostały przydzielone na stosie po wpisaniu funkcji). Dlatego EBP nazywa się wskaźnikiem frame pointer , ponieważ wskazuje na środek ramka wywołania funkcji.

Po zakończeniu, funkcja musi ustawić ESP na wartość EBP (która dealokuje zmienne lokalne ze stosu i wyświetla wpis EBP na top of the stack), następnie pop the old EBP value from the stack, and then the function returns (popping the return address into EIP).

 67
Author: David R Tribble,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-28 22:33:19

Masz rację. Wskaźnik stosu wskazuje na górny element stosu, a wskaźnik bazowy na "poprzedni" wierzchołek stosu przed wywołaniem funkcji.

Podczas wywoływania funkcji Dowolna zmienna lokalna zostanie zapisana na stosie, a wskaźnik stosu zostanie zwiększony. Po powrocie z funkcji wszystkie zmienne lokalne na stosie wychodzą poza zakres. Można to zrobić, ustawiając wskaźnik stosu z powrotem do wskaźnika bazowego (który był" poprzedni " górny przed wywołaniem funkcji).

Robienie alokacji pamięci w ten sposób jest Bardzo, bardzo szybko i sprawnie.

 12
Author: Robert Cartaino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-08 18:48:39

EDIT: aby uzyskać lepszy opis, Zobacz x86 Disassembly/Functions and Stack Frames w Wikibooku o x86 assembly. Staram się dodać kilka informacji, które mogą Cię zainteresować za pomocą Visual Studio.

Przechowywanie wywołującego EBP jako pierwszej zmiennej lokalnej nazywa się standardową ramką stosu i może być używana dla prawie wszystkich konwencji wywołujących w systemie Windows. Istnieją różnice, czy wywołujący lub callee deallocuje przekazane parametry i które parametry są przekazywane w rejestry, ale są ortogonalne do standardowego problemu ramki stosu.

Mówiąc o programach Dla Windows, prawdopodobnie możesz użyć Visual Studio do skompilowania kodu C++. Należy pamiętać, że Microsoft używa optymalizacji o nazwie Frame Pointer pominięcie, które sprawia, że prawie niemożliwe do zrobienia chodzić stosu bez użycia biblioteki dbghlp I pliku PDB dla pliku wykonywalnego.

Pominięcie wskaźnika Ramki oznacza, że kompilator nie przechowuje starego EBP w standardowym miejscu i używa EBP rejestruje coś innego, dlatego trudno jest znaleźć dzwoniącego EIP, nie wiedząc, ile miejsca potrzebują zmienne lokalne dla danej funkcji. Oczywiście Microsoft zapewnia API, które pozwala na wykonywanie spacerów stosu nawet w tym przypadku, ale Wyszukiwanie bazy danych tabeli symboli w plikach PDB zajmuje zbyt dużo czasu dla niektórych przypadków użycia.

Aby uniknąć FPO w jednostkach kompilacji, musisz unikać używania /O2 lub musisz wyraźnie dodać /Oy - do FLAG kompilacji C++ w Twoim projekty. Prawdopodobnie łączysz się z runtime C lub C++, który używa FPO w konfiguracji Wydania, więc będziesz miał trudności z wykonywaniem spacerów stosu bez dbghlp.dll.

 6
Author: wigy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-09 09:09:02

Po pierwsze, wskaźnik stosu wskazuje na dno stosu, ponieważ stosy x86 budują się od wysokich wartości adresów do niższych wartości adresów. Wskaźnik stosu jest punktem, w którym następne wywołanie push (lub call) umieści następną wartość. Jego działanie jest równoważne instrukcji C / C++:

 // push eax
 --*esp = eax
 // pop eax
 eax = *esp++;

 // a function call, in this case, the caller must clean up the function parameters
 move eax,some value
 push eax
 call some address  // this pushes the next value of the instruction pointer onto the
                    // stack and changes the instruction pointer to "some address"
 add esp,4 // remove eax from the stack

 // a function
 push ebp // save the old stack frame
 move ebp, esp
 ... // do stuff
 pop ebp  // restore the old stack frame
 ret

Wskaźnik bazowy znajduje się na górze bieżącej ramki. ebp zazwyczaj wskazuje na twój adres zwrotny. ebp + 4 wskazuje na pierwszy parametr funkcji (lub tę wartość metody klasowej). ebp-4 wskazuje na pierwszą zmienną lokalną funkcji, zwykle starą wartość ebp, dzięki czemu można przywrócić poprzedni wskaźnik ramki.

 5
Author: jmucchiello,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-08 18:59:05

Dawno nie robiłem programowania asemblera, ale ten link może się przydać...

Procesor posiada zbiór rejestrów, które służą do przechowywania danych. Niektóre z nich są wartościami bezpośrednimi, podczas gdy inne wskazują obszar w pamięci RAM. Rejestry są zwykle używane do pewnych konkretnych działań i każdy operand w assembly będzie wymagał pewnej ilości danych w określonych rejestrach.

Wskaźnik stosu jest najczęściej używany podczas wywoływania innych procedur. W nowoczesnych kompilatorach, garść danych zostanie najpierw wrzucona na stos, a następnie adres zwrotny, więc system będzie wiedział, gdzie zwrócić, gdy zostanie poinformowany o powrocie. Wskaźnik stosu wskaże następne miejsce, w którym nowe dane mogą być wepchnięte do stosu, gdzie pozostaną, dopóki nie zostaną ponownie wyświetlone.

Rejestry bazowe lub rejestry segmentów po prostu wskazują przestrzeń adresową dużej ilości danych. W połączeniu z drugim regiserem, wskaźnik bazowy podzieli pamięć na ogromne blokuje, podczas gdy drugi rejestr wskaże element w tym bloku. Wskaźniki bazowe do nich wskazują bazę bloków danych.

Należy pamiętać, że montaż jest bardzo specyficzny dla procesora. Strona, do której podlinkowałem, zawiera informacje o różnych typach procesorów.

 1
Author: Wim ten Brink,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-08 18:50:18

Edit tak, to jest w większości złe. Opisuje coś zupełnie innego na wypadek gdyby ktoś był zainteresowany :)

Tak, wskaźnik stosu wskazuje na wierzchołek stosu(niezależnie od tego, czy jest to pierwsza pusta lokalizacja stosu, czy ostatnia pełna lokalizacja, której nie jestem pewien). Wskaźnik bazowy wskazuje na lokalizację pamięci wykonywanej instrukcji. Jest to na poziomie kodĂłw opcodes - najbardziej podstawowa Instrukcja, ktĂłra moĹźna uzyskaÄ ‡ na komputerze. Każdy kod opcode i jego parametry są przechowywane w Miejsce Pamięci. Jedna linia C lub c++ lub C# może być przetłumaczona na jeden kod opcode, lub Sekwencja dwóch lub więcej w zależności od stopnia złożoności. Są one zapisywane w pamięci programu sekwencyjnie i wykonywane. W normalnych warunkach wskaźnik bazowy jest zwiększany o jedną instrukcję. Do sterowania programem (GOTO, IF, itd.) Można go wielokrotnie zwiększać lub po prostu zastępować następnym adresem pamięci.

W tym kontekście funkcje są przechowywane w pamięci programu pod określonym adresem. Gdy funkcja jest wywoływana, pewne informacje są popychane na stos, który pozwala programowi znaleźć jego było z powrotem do miejsca, z którego funkcja została wywołana, jak również parametry do funkcji, a następnie adres funkcji w pamięci programu jest wciśnięty do wskaźnika bazowego. W następnym cyklu zegara komputer zaczyna wykonywać instrukcje z tego adresu pamięci. Następnie w pewnym momencie powróci do miejsca pamięci po instrukcji, która wywołała funkcję i kontynuuje od tam.

 -3
Author: Stephen Friederichs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-08 19:11:23

Esp oznacza "Extended Stack Pointer".....ebp dla "czegoś podstawowego wskaźnika"....i eip dla "coś Instrukcja Pointer"...... Wskaźnik stosu wskazuje na adres przesunięcia segmentu stosu. Wskaźnik bazowy wskazuje na adres przesunięcia dodatkowego segmentu. Wskaźnik instrukcji wskazuje na offsetowy adres segmentu kodu. Co do segmentów...są to małe podziały 64KB obszaru pamięci procesorów.....Proces ten znany jest jako segmentacja pamięci. Mam nadzieję, że to post był pomocny.

 -3
Author: Adarsha Kharel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-14 18:54:40