Нужна помощь в понимании макета стека

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

Вот представление вызова функции, которую я пытаюсь составить:

void Function(unsigned long long a, const void * b, unsigned int c) {
    printf("a=0x%llX, b=%p, c=0x%X\n", a, b, c);
    _asm { int 3 }; /* Because I don't have stepping or dynamic breakpoints implemented yet */
 }
 int main(int argc, char* argv[]) {
     Function(2, (void*)0x7A3FE8, 0x2004);
     return 0;
 }

Вот что функция (неудивительно) выводит на консоль:

a=0x2, c=0x7a3fe8, c=0x2004

Это трассировка стека, сгенерированная в точке останова (отладчик ловит точку останова, и там я пытаюсь пройтись по стеку):

0x3EF5E0: 0x10004286 /* previous pc */
0x3EF5DC: 0x3EF60C   /* previous fp */
0x3EF5D8: 0x7A3FE8   /* arg b --> Wait... why is b _above_ c here? */
0x3EF5D4: 0x2004     /* arg c */
0x3EF5D0: 0x0        /* arg a, upper 32 bit */
0x3EF5CC: 0x2        /* arg a, lower 32 bit */

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

ULONGLONG stackframe_top = 0;
m_frame->get_base(&stackframe_top); /* IDiaStackFrame */

/* dump 30 * 4 bytes */
for (DWORD i = 0; i < 30; i++)
{
    ULONGLONG address = stackframe_top - (i * 4);
    DWORD value;
    SIZE_T read_bytes;
    if (ReadProcessMemory(m_process, reinterpret_cast<LPVOID>(address), &value, sizeof(value), &read_bytes) == TRUE)
    {
        debugprintf(L"0x%llX: 0x%X\n", address, value); /* wrapper around OutputDebugString */
    }
}

Я собираю тестовую программу без какой-либо оптимизации в обновлении vs2015 3.

Я подтвердил, что я действительно собираю его как cdecl, посмотрев в pdb с dia2dump образец приложения. Я не понимаю, что заставляет стек выглядеть так, он не соответствует ничему из того, что я изучил, и не соответствует документации, предоставленной Microsoft.
Я также много раз проверял Google (включая вики-страницы osdev, сообщения в блоге msdn и т. Д.) И проверял свои (на данный момент, вероятно, устаревшие) книги по программированию 32-битных сборок x86 (которые были выпущены до появления 64-битных процессоров).,

Заранее большое спасибо за любые объяснения или ссылки!

1 ответ

Я как-то неправильно понял, где аргументы вызова функции оказываются в памяти по сравнению с основанием стекового фрейма, как указал Рэймонд. Это фиксированный фрагмент кода:

ULONGLONG stackframe_top = 0;
m_frame->get_base(&stackframe_top); /* IDiaStackFrame */

/* dump 30 * 4 bytes */
for (DWORD i = 0; i < 30; i++)
{
    ULONGLONG address = stackframe_top + (i * 4); /* <-- Read before the stack frame */
    DWORD value;
    SIZE_T read_bytes;
    if (ReadProcessMemory(m_process, reinterpret_cast<LPVOID>(address), &value, sizeof(value), &read_bytes) == TRUE)
    {
        debugprintf(L"0x%llX: 0x%X\n", address, value); /* wrapper around OutputDebugString */
    }

}

Другие вопросы по тегам