Что такое базовый указатель и указатель стека? На что они указывают?



используя исходя из Википедии, в которой DrawSquare () вызывает DrawLine (),



alt text



(обратите внимание, что эта диаграмма имеет высокие адреса внизу и низкие адреса вверху.)



может ли кто-нибудь объяснить мне, что ebp и esp в этом контексте?



из того, что я вижу, я бы сказал, что указатель стека всегда указывает на верхнюю часть стека, а базовый указатель на начало текущей функции? Или что?





edit: я имею в виду это в контексте программ windows



edit2: а как же eip работы тоже?



edit3: у меня есть следующий код из 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


все они кажутся dwords, таким образом, принимая 4 байта каждый. Так что я вижу, что есть разрыв от hInstance до var_4 4 байта. Что это такое? Я предполагаю, что это обратный адрес, как можно увидеть в Википедии фото?





(Примечание редактора: удалена длинная цитата из ответа Майкла, которая не относится к вопросу, но следующий вопрос был отредактирован):



это потому, что поток вызова функции:



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


мой вопрос (последний, я надеюсь!) теперь, что именно происходит с момента появления аргументов функции, которую я хочу вызвать, до конца пролога? Я хочу знать, как ebp, esp развиваются во время этих моменты (я уже понял, как работает пролог, я просто хочу знать, что происходит после того, как я нажал аргументы в стеке и перед прологом).

981   8  

8 ответов:

esp это, как вы говорите, это, в верхней части стека.

ebp устанавливается esp в начале функции. Параметры функции и локальные переменные доступны путем сложения и вычитания, соответственно, постоянного смещения от ebp. Все соглашения о вызовах x86 определяют ebp как сохраненный через вызовы функций. ebp сам по себе фактически указывает на базовый указатель предыдущего кадра, который позволяет стеку ходить в отладчике и просматривать другие рамки локальных переменных для работы.

большинство прологов функций выглядят примерно так:

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.

тогда позже в функции у вас может быть такой код (предполагая, что обе локальные переменные составляют 4 байта)

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

FPO или пропуск указателя кадра оптимизация, которую вы можете включить, фактически устранит это и использует ebp как еще один регистр и доступ к местным жителям непосредственно от esp, но это делает отладку немного сложнее, поскольку отладчик больше не может напрямую обращаться к кадрам стека предыдущих вызовов функций.

EDIT:

для вашего обновленного вопроса отсутствуют две записи в стеке:

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

это потому, что поток вызова функции:

  • Push параметры (hInstance и т. д.)
  • вызов функции, которая толкает обратный адрес
  • Push ebp
  • выделите место для местных жителей

ESP-это текущий указатель стека, который будет меняться каждый раз, когда слово или адрес нажимается или выскакивает из стека. EBP-это более удобный способ для компилятора отслеживать параметры функции и локальные переменные, чем использовать ESP напрямую.

Как правило (и это может варьироваться от компилятора к компилятору), все аргументы функции помещаются в стек (обычно в обратном порядке, в котором они объявлены в функции прототипа, но это варьируется). Затем вызывается функция, которая помещает обратный адрес (EIP) в стек.

при входе в функцию старое значение EBP помещается в стек, а EBP устанавливается в значение ESP. Затем ESP уменьшается (потому что стек растет вниз в памяти), чтобы выделить пространство для локальных переменных и временных функций функции. С этого момента во время выполнения функции аргументы функции располагаются в стеке с положительным значением смещения от EBP (потому что они были выдвинуты до вызова функции), а локальные переменные расположены на отрицательных смещениях от EBP (потому что они были выделены в стеке после записи функции). Вот почему EBP называется указатель фрейма, потому что он указывает к центру кадр вызова функции.

при выходе все, что должна сделать функция, это установить ESP на значение EBP (которое освобождает локальные переменные от стек, и предоставляет запись EBP в верхней части стека), затем всплывает старое значение EBP из стека, а затем функция возвращает (выталкивая обратный адрес в EIP).

вы все правильно поняли. Указатель стека указывает на верхний элемент стека, а базовый указатель указывает на "предыдущая" вершина стека перед вызовом функции.

при вызове функции любая локальная переменная будет сохранена в стеке, а указатель стека будет увеличен. Когда вы возвращаетесь из функции, все локальные переменные в стеке выходят из области видимости. Для этого установите указатель стека обратно в базовый указатель (который был "предыдущая" вершина перед вызовом функции).

делать выделение памяти таким образом очень,очень быстро и эффективно.

EDIT: для лучшего описания см. x86 разборка / функции и стековые кадры в WikiBook о сборке x86. Я пытаюсь добавить некоторые сведения, которые могут вас заинтересовать в использовании Visual Studio.

сохранение вызывающего EBP в качестве первой локальной переменной называется стандартным стековым фреймом, и это может использоваться почти для всех соглашений о вызовах в Windows. Существуют различия в том, освобождает ли вызывающий или вызываемый объект переданные параметры и какие параметры передаются в регистрах, но они ортогональны к стандартной задаче кадра стека.

говоря о программах Windows, вы, вероятно, можете использовать Visual Studio для компиляции кода C++. Имейте в виду, что Microsoft использует оптимизацию, называемую пропуском указателя кадра, что делает практически невозможным выполнение обхода стека без использования библиотеки dbghlp и файла PDB для исполняемого файла.

Это опущение указателя кадра означает, что компилятор не сохраняет старый EBP на стандартное место и использует регистр EBP для чего-то еще, поэтому вам трудно найти вызывающий EIP, не зная, сколько места требуется локальным переменным для данной функции. Конечно, Microsoft предоставляет API, который позволяет выполнять Stack-walks даже в этом случае, но поиск базы данных таблиц символов в файлах PDB занимает слишком много времени для некоторых случаев использования.

чтобы избежать FPO в ваших единицах компиляции, вам нужно избегать использования /O2 или явно добавлять /Oy - в C++ флаги компиляции в ваших проектах. Вероятно, вы ссылаетесь на среду выполнения C или C++, которая использует FPO в конфигурации выпуска, поэтому вам будет трудно выполнять прогулки по стеку без dbghlp.файл DLL.

прежде всего, указатель стека указывает на нижнюю часть стека, так как стеки x86 строятся из высоких значений адресов в более низкие значения адресов. Указатель стека-это точка, в которой при следующем вызове push (или вызове) будет размещено следующее значение. Это операция эквивалентна оператору 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

базовый указатель текущего кадра. ebp обычно указывает на ваш обратный адрес. ebp+4 указывает на первый параметр вашей функции (или это значение класса метод.) ebp-4 указывает на первую локальную переменную вашей функции, обычно старое значение ebp, поэтому вы можете восстановить предыдущий указатель кадра.

давно я не занимался ассемблерным программированием, но этой ссылке может быть полезно...

процессор имеет набор регистров, которые используются для хранения данных. Некоторые из них являются прямыми значениями, в то время как другие указывают на область в ОЗУ. Регистры, как правило, используются для определенных конкретных действий, и каждый операнд в сборке потребует определенного количества данных в конкретных регистрах.

указатель стека в основном используется при вызове другие методы. С помощью современных компиляторов сначала в стек будет сброшена куча данных, а затем обратный адрес, чтобы система знала, куда вернуться, как только ей будет сказано вернуться. Указатель стека будет указывать на следующее место, где новые данные могут быть помещены в стек, где они будут оставаться до тех пор, пока не будут возвращены обратно.

базовые регистры или сегментные регистры просто указывают на адресное пространство большого объема данных. В сочетании со вторым регистратором базовый указатель будет разделите память на огромные блоки, в то время как второй регистр будет указывать на элемент внутри этого блока. Базовые указатели для этого указывают на базу блоков данных.

имейте в виду, что сборка очень специфична для ЦП. Страница, на которую я ссылался, содержит информацию о различных типах процессоров.

Edit Да, это в основном неправильно. Он описывает что-то совершенно другое в случае, если кто-то заинтересован :)

да, указатель стека указывает на верхнюю часть стека (будь то первое пустое место стека или последнее полное, в котором я не уверен). Базовый указатель указывает на место в памяти выполняемой инструкции. Это на уровне опкодов-самая основная инструкция, которую вы можете получить на компьютере. Каждый код операции и его параметры хранятся в памяти. Одна строка C или C++ или C# может быть переведена в один код операции или последовательность из двух или более в зависимости от того, насколько она сложна. Они записываются в память программы последовательно и выполняются. В обычных условиях базовый указатель увеличивается на одну инструкцию. Для управления программой (GOTO, IF и т. д.) Он может быть увеличен несколько раз или просто заменен следующим адресом памяти.

в этом контексте, функции хранятся в память программы по определенному адресу. Когда функция вызывается, определенная информация помещается в стек, который позволяет программе найти ее обратно туда, откуда была вызвана функция, а также параметры функции, затем адрес функции в памяти программы помещается в базовый указатель. На следующем такте компьютер начинает выполнять инструкции с этого адреса памяти. Затем в какой-то момент он вернется в ячейку памяти после инструкции, что вызывается функция и продолжается оттуда.

esp означает "расширенный указатель стека".....ebp для"чего-то базового указателя"....и eip для"чего-то указателя инструкции"...... Указатель стека указывает на адрес смещения сегмента стека. Базовый указатель указывает на адрес смещения дополнительного сегмента. Указатель инструкции указывает на адрес смещения сегмента кода. Теперь о сегментах...они представляют собой небольшие 64KB подразделения области памяти процессоров.....Этот процесс известен как сегментация памяти. Я надеюсь, что это сообщение было полезно.

Comments

    Ничего не найдено.