Хранить данные под указателем стека? - PullRequest
3 голосов
/ 22 февраля 2010

Глядя на дизассемблирование (вместе с трассировкой инструкций) ld.so, установленного в Ubuntu 9.04, я клянусь, что время от времени вижу данные, хранящиеся ниже указателя стека (то есть за пределами стека). Это кажется мне безумным, но, возможно, это чаще, чем я думаю. Это часто случается?

Вот что я вижу:

ebp: 0xBF8269E8, esp: 0xBF8269DC

c98:       8b 45 f0                mov    -0x10(%ebp),%eax
c9b:       8d 14 06                lea    (%esi,%eax,1),%edx
c9e:       8b 83 28 03 00 00       mov    0x328(%ebx),%eax
ca4:       3b 50 04                cmp    0x4(%eax),%edx

Ответы [ 2 ]

1 голос
/ 22 февраля 2010

Обработчики сигналов должны иметь возможность создавать фрейм стека в любое время. Поэтому вам всегда нужно следовать протоколу стека вашего ABI.

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

1 голос
/ 22 февраля 2010

Что именно заставляет вас верить, что вещи хранятся ниже указателя stack . Все, что я вижу, это отрицательное смещение от ebp, которое является указателем frame .

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

  • По обе стороны от ebp находятся параметры, передаваемые этой функции (выше ebp), и локальные данные для этой функции (ниже ebp). Использование -0x10(%ebp) просто означает, что вы что-то делаете с локальной переменной.
  • Легко вернуться к предыдущему кадру стека, просто загрузив %esp с %ebp и вернувшись.

Конечно, вы можете достичь чего-то ниже %esp, но это будет зависеть от загружаемых данных, которые на самом деле не показаны в вашем примере.

«Диаграмма» может помочь:

     +------------------------+
     | Parameters passed to x |
     +------------------------+
     | Return address         |
%ebp +------------------------+
     | Locals for x           |
%esp +------------------------+

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

  • вызывающая сторона помещает параметры в стек (push, push, ...).
  • вызывающий абонент помещает текущий %ebp в стек (push %ebp).
  • вызывающий груз %ebp с %esp% (mov %ebp, %esp).
  • звонящий звонит вызываемому абоненту (call XYZ).
  • вызываемый абонент выделяет место для местных жителей (sub %esp,N).
  • вызываемый может затем использовать (%ebp+N) для параметров, (%ebp-N) для местных жителей.

А для возврата:

  • загружаемых вызовов %esp с %ebp (mov %esp, %ebp).
  • вызываемый абонент возвращается к вызывающему абоненту (ret).
  • вызывающий абонент выскакивает предыдущий %ebp из стека (pop %ebp).
  • вызывающий абонент очищает секцию параметров (add %esp,N).
...