Куда указывает указатель кадра после установки? - PullRequest
2 голосов
/ 15 октября 2010

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

    0x08048b29 <func+0>: push   %ebp
    0x08048b2a <func+1>: mov    %esp,%ebp
    0x08048b2c <func+3>: push   %ebx
    ...
    0x08048b30 <phase_2+7>: lea    -0x28(%ebp),%eax

В инструкции по выходу я понимаю, что% eax получает значение 0x28 перед% ebp, но где это?именно так?Это 0x8048b29 - 0x28 (0x28 до начала функции) или как?

Ответы [ 2 ]

1 голос
/ 01 декабря 2010

Посмотрите на это из контекста , вызывающего этой функции. Код, который делает это выглядит примерно так:

caller+...: push argN
caller+...: ...
caller+...: push arg0
caller+...: call func

т.е. аргументы помещаются в стек в таком порядке, что при входе в func() стек будет иметь следующий формат:

[esp+(N*4)] : argN
...         : arg(N-1)...arg2
[esp+4    ] : arg1
[esp      ] : <return address to caller()+...>

Затем вы выполняете последовательность push %ebp; mov %esp, %ebp, которая меняет %esp (на -4), так что ваш макет теперь:

[ebp+4+(N*4)][esp+(N*4)] : argN
...                      : arg(N-1)...arg2
[ ebp+8     ][esp+8    ] : arg1
[ ebp+4     ][esp+4    ] : <return address to caller()+...>
[ ebp       ][esp      ] : <saved %ebp of caller>

Затем код отправляет еще несколько регистров в стек - так как он увеличивается каждый раз, когда %esp изменяется на -4. В конечном итоге (что вы не показали в своей разборке, но она будет там) у вас будет инструкция subl $..., %esp. Это то, что выделяет пространство для ваших локальных переменных. Окончательный макет стека выглядит примерно так:

[ebp+4+(N*4)][         ] : argN
...                      : arg(N-1)...arg2
[ ebp+8     ][         ] : arg1
[ ebp+4     ][         ] : <return address to caller()+...>
[ ebp       ][         ] : <saved %ebp of caller>
[ ebp-4     ][         ] : <saved %ebx of caller>
[ ebp-8     ][         ] : ...
...                      : region for local variables
[ ebp-??    ][ esp     ] : end of stack for func()

Любой адрес между [esp ... ebp-4] находится внутри так называемого стекового фрейма для вашей функции и содержит регистр, сохраненный от имени вызывающей стороны (например, ebx в случае показанной вами разборки), или локальные переменные .

Следовательно, когда в вашем коде вы видите какой-либо доступ к %ebp - XX, он находится в пространстве локальной переменной, если вы видите %ebp + YY, он находится внутри пространства, содержащего аргументы функции.

1 голос
/ 15 октября 2010

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

(1) 0x08048b29 <func+0>: push %ebp

Новая вершина стека копируется в ebp, чтобы служить базовым указателем в этой функции.

(2) 0x08048b2a <func+1>: mov %esp,%ebp

Наконец, ebx сохраняется, что, вероятно, было переменной, переданной функции в регистре.

(3) 0x08048b2c <func+3>: push %ebx

После всего этого кода ввода функции регистр ebp указывает на середину стека. Над ним в стеке (по отношению к новейшим отправленным элементам) находится значение ebx, которое было помещено в # 3 выше. Под ним в стеке находится старый ebp из вызывающей функции (сохраненный в # 1 выше) и, что немаловажно, любые аргументы, передаваемые этой функции через стек (делается до вызова функции).

(4) lea -0x28(%ebp),%eax

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

...