Зачем нам нужно «pushl% ebp» в начале каждой программы сборки? [Дубликат]

@Pyrolistical

, так как исходный первый статический класс myclass не полностью построен ... результат i get is

null null testInitialize.MyObject@70f9f9d8 null

33
задан peSHIr 13 September 2010 в 12:23
поделиться

4 ответа

Кадр стека x86-32 создается путем выполнения

function_start:
    push ebp
    mov ebp, esp

, поэтому он доступен через ebp и выглядит как

ebp+00 (current_frame) : prev_frame
ebp+04                 : return_address
                         ....
prev_frame             : prev_prev_frame
prev_frame+04          : prev_return_address

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

10
ответ дан Abyx 21 August 2018 в 00:42
поделиться
  • 1
    Это кажется неправильным для меня - вместо этого не адрес возврата, найденный в ebp + 04 (см. Наиболее ответный ответ)? – Suma 5 September 2011 в 12:33
  • 2
    @Suma, да, это так - исправлено. – Abyx 5 September 2011 в 18:56

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

Фрейм стека для каждой подпрограммы делится на три части: функциональные параметры, обратный указатель на предыдущий стек кадр и локальные переменные.

Часть 1: Параметры функции

Эта часть фрейма стека подпрограммы настроена вызывающим. Используя команду «push», вызывающий пользователь выталкивает параметры в стек. Различные языки могут подталкивать параметры в разных порядках. C, если я правильно помню, толкает их справа налево. То есть, если вы вызываете ...

foo (a, b, c);

Вызывающий преобразует это значение в ...

push c
push b
push a
call foo

Поскольку каждый элемент помещается в стек, стек растет. То есть регистр указателя стека уменьшается на четыре (4) байта (в 32-битном режиме), и элемент копируется в ячейку памяти, на которую указывает регистр стека. Обратите внимание, что команда «вызов» будет неявно нажать адрес возврата в стеке. Очистка параметров будет рассмотрена в части 5.

Часть 2: указатель обратного отсчета стека

На данный момент выдается инструкция «вызов», и мы сейчас находимся в начало вызванной процедуры. Если мы хотим получить доступ к нашим параметрам, мы можем получить к ним доступ, как ...

[esp + 0]   - return address
[esp + 4]   - parameter 'a'
[esp + 8]   - parameter 'b'
[esp + 12]  - parameter 'c'

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

push ebp        ; save previous stackbase-pointer register
mov  ebp, esp   ; ebp = esp

Иногда вы может видеть это, используя только инструкцию «ENTER».

Часть 3: Резервное пространство для локальных переменных

Локальные переменные сохраняются в стеке. Так как стек растет, мы вычитаем несколько байтов (достаточно для хранения наших локальных переменных):

sub esp, n_bytes ; n_bytes = number of bytes required for local variables

Часть 4: Объединим все это. Доступ к параметрам осуществляется с помощью регистра указателя стека ...

[ebp + 16]  - parameter 'c'
[ebp + 12]  - parameter 'b'
[ebp + 8]   - parameter 'a'
[ebp + 4]   - return address
[ebp + 0]   - saved stackbase-pointer register

Доступ к локальным переменным осуществляется с помощью регистра указателя стека ...

[esp + (# - 4)] - top of local variables section
[esp + 0]       - bottom of local variables section

Часть 5: Очистка стоп-кадра

Когда мы покидаем подпрограмму, необходимо очистить стековый фрейм.

mov esp, ebp   ; undo the carving of space for the local variables
pop ebp        ; restore the previous stackbase-pointer register

Иногда вы можете увидеть инструкцию «LEAVE», заменяющую эти две команды.

В зависимости от языка, который вы использовали, вы можете увидеть одну из двух форм инструкции «RET».

ret
ret <some #>

Какой бы выбор ни выбрали, будет зависеть от выбора языка (или стиля, который вы хотите следовать если писать на ассемблере). Первый случай указывает на то, что вызывающий отвечает за удаление параметров из стека (с примером foo (a, b, c) он будет делать это через ... add esp, 12), и это способ «C» Это. Второй случай указывает, что команда return будет выталкивать # слова (или # байты, я не могу вспомнить, какой) из стека, когда он возвращается, тем самым удаляя параметры из стека. Если я правильно помню, это стиль, используемый Паскалем.

Это долго, но я надеюсь, что это поможет вам лучше понять стековые кадры.

115
ответ дан Alexander Malakhov 21 August 2018 в 00:42
поделиться
  • 1
    +1 - Очень хороший ответ. Можете ли вы порекомендовать несколько хороших книг для этих частей? – Abid Rahman K 7 February 2013 в 16:13
  • 2
    Поскольку я редко читаю книги по программированию, у меня нет никаких рекомендаций. Информация, приведенная выше, - это всего лишь некоторые вещи, которые я помню из школы несколько десятилетий назад, и материал, который я выбрал из экспериментов на протяжении многих лет. – Sparky 7 February 2013 в 16:29
  • 3
    @AbidRahmanK: Я рекомендую этот курс в Coursera coursera.org/course/hwswinterface Это очень хороший курс, и его следует воспринимать всерьез. – jyz 13 May 2013 в 12:57
  • 4
    @Sparky его довольно объяснение, которое вы дали, и у вас есть преимущество от моего конца, но только сомнение в доступе к параметру "C" с помощью [ebp + 16], подсчет с моей стороны говорит, что это должно быть [ebp + 20], нажатие старого ebp на стек приведет к тому, что esp будет уменьшаться на четыре, а после этого ebp = esp и получить доступ к более раннему ebp. [ebp + 4], Пожалуйста, поправьте меня, если я ошибаюсь. – Amit Singh Tomar 6 June 2013 в 10:58
  • 5
    @naxa: Фрейм стека представляет собой концепцию, полезную при логическом разделении переменных, хранящихся в стеке. Это настолько полезно, что относительно мало случаев, когда вы НЕ хотели бы его использовать. Процессор x86 не использует его, но он настоятельно рекомендует его. Например: в 16-битном режиме регистры SP и BP относятся к числу немногих регистров, которые принимают смещение. Во всех режимах инструкция CALL автоматически подталкивает адрес возврата в стек, а команда RET автоматически выдает адрес возврата из стека. Надеюсь, это поможет. – Sparky 10 April 2014 в 17:00

Кадр стека x86 может использоваться компиляторами (в зависимости от компилятора) для передачи параметров (или указателей на параметры) и возвращаемых значений. См. этот

0
ответ дан Community 21 August 2018 в 00:42
поделиться

Это зависит от используемой операционной системы и языка. Причина в том, что в ASM нет общего формата для стека, единственное, что делает стек в ASM, - это сохранить обратный адрес при выполнении подпрограммы перехода. При выполнении функции return-from-sub адрес выбирается из стека и помещается в программу-счетчик (ячейка памяти, в которой должна быть записана следующая инструкция выполнения ЦП)

Вам нужно будет проконсультироваться с вашим документацию для используемого компилятора.

2
ответ дан UnixShadow 21 August 2018 в 00:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: