2010-10-14 3 views
2

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

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

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

ответ

1

Регистр 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 выше) и MOST важно ... любые аргументы, переданные этой функции через стек (выполняемые до вызова функции).

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

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

+0

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

1

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

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, он находится в пространстве, содержащем аргументы функции.

Смежные вопросы