Linux Stack - main (), почему ebp не помещается в стек - PullRequest
0 голосов
/ 13 декабря 2011

Я просматриваю стек в начале main, но ebp main отсутствует.

Я объявил переменную, чтобы проверить, где она будет находиться в стеке, оказывается, что есть нулимежду этой переменной и адресом возврата n __libc_start_main!

Система, которую я использую

I'm using fedora Linux 3.1.2-1.fc16.i686 
ASLR is disabled.
Debugging with GDB.

Вот код:

void main(){

        char ret ='a';

}

Информация о регистре:

(gdb)
eax            0x1      1
ecx            0xbffff5f4       -1073744396
edx            0xbffff584       -1073744508
ebx            0x2dbff4 2998260
esp            0xbffff554       0xbffff554
**ebp            0xbffff558       0xbffff558**
esi            0x0      0
edi            0x0      0
eip            0x804839a        0x804839a <main+6>

stack

(gdb) x/8xw $esp
0xbffff554: 0x00000000(local var) 0x00000000(missing ebp!) 0x0014d6b3(return to libc_start)      0x00000001

0xbffff564:     0xbffff5f4      0xbffff5fc      0x00131fc4      0x0000082d

Единственное, о чем я могу подумать, это то, что пролог функции libc_start_main по какой-то причине не толкает ebp главного!


Редактирование 1:

- скомпилировано без Opatmization просто (gcc -ggdb file file.c)

Сборка основного (gcc версия 4.6.2 20111027)

 push   %ebp

 mov    %esp,%ebp
 sub    $0x10,%esp
 movb   $0x61,-0x1(%ebp)
 leave
 ret

Точка останова локальной переменной для просмотра стека показывает то же самое, что переменная, за которой следуют нули, а затем возврат к libc_start

Ответы [ 3 ]

2 голосов
/ 13 декабря 2011

Нет требования для конкретного соглашения о вызовах, которое будет использоваться в коде сборки, сгенерированном компилятором. Вот почему это называется соглашение , а не требование: -)

В любом случае вам следует помнить, что для «нормального» соглашения о вызовах x86 для C требуется, чтобы сама функция выполняла настройку и удаление кадра стека. Другими словами, это ответственность main вместо кода запуска (код, который обычно запускается до вашего main для настройки среды выполнения C, такой как настройка стека, создание argc/argv, любая предварительная инициализация библиотеки и т. д.).

Кроме того, ebp, помещенный в стек, равен предыдущему значению ebp до построения текущего кадра стека.

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

Это можно увидеть, скомпилировав фрагмент кода с помощью gcc -S:

main:
    pushl    %ebp              ; Push PREVIOUS ebp.
    movl     %esp, %ebp        ; Load ebp for variable access.
    subl     $16, %esp         ; Allocate space on stack.

    movb     $97, -1(%ebp)     ; Store 'a' into variable.

    leave                      ; Tear down frame and return.
    ret

Первые три строки и последние две являются зеркальными отображениями друг друга, кода установки и разрыва. В этом случае велика вероятность того, что для кода запуска было установлено значение ebp равное нулю, возможно, потому, что ему было все равно - ему не нужно беспокоиться о соглашениях о вызовах, за исключением того, что argc и argv существуют. .

2 голосов
/ 13 декабря 2011

Если вы компилируете без оптимизации, вы почти наверняка обнаружите, что ebp / rbp фактически помещается в стек и затем настраивается на основе esp / rsp.Однако это делается самим main, а не libc, как вы предполагаете.

Вот код сборки, созданный gcc 4.4.5:

main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        movb    $97, -1(%rbp)
        leave
        ret
       .cfi_endproc

Есливы компилируете опции оптимизации, вы можете обнаружить, что оптимизировано все тело main (gcc -O3):

main:
.LFB0:
        .cfi_startproc
        rep
        ret
        .cfi_endproc

Вместо того, чтобы гадать, почему бы не посмотреть на разборку (например, в gdb) чтобы увидеть, что происходит в вашем конкретном случае?

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

Наконец, вас не должно удивлять, когда вы видите очевидные промежутки между данными в стеке, так как стек подлежит выравниванию:

   -mpreferred-stack-boundary=num
       Attempt to keep the stack boundary aligned to a 2 raised to num
       byte boundary.  If -mpreferred-stack-boundary is not specified, the
       default is 4 (16 bytes or 128 bits).
0 голосов
/ 13 декабря 2011

Если вы компилируете для x86_64, то ebp / rbp будет сохранено.Это означает, что main() должен сохранить его, если ему нужно его использовать.Если нет, то нет необходимости в сохранении старого значения регистра вызывающим или вызывающим абонентом.

См. Раздел 3.2 AMD64 ABI для получения дополнительной информации, если вы заинтересованы.

...