Re[8]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: TaniaDen  
Дата: 09.12.03 23:32
Оценка: 9 (1)
Здравствуйте, Sergey, Вы писали:

S>Выкидывать-то зачем? Пусть валяется, никому не помешает.


Скорее всего "выкинуть" — изменить ESP

Вобщем, так работает (запускала в Wnidows 2000 SP4, хотя имхо должно работать везде на x86, вплодь до DOS-а, только регистры поменять на 16-ти разрядные..):

#include <stdio.h>
#include <malloc.h>

int __cdecl f1(char* str, ...)
{
    // до начала выполнения стек выглядит так (сверху вниз):
    //  
    // параметры
    // адрес возврата (слово)
    // -----------------
    // свободно
    //
    // esp содержит указатель на начало последнего 
    // слова в стеке, т.е. на начало адреса возврата
    //
    // пролог функции:
    // push ebp
    // mov  ebp, esp
    //
    // теперь стек такой:
    //
    // параметры
    // адрес возврата (слово)
    // регистр ebp (слово)
    // -----------------
    // свободно
    //
    // а ebp указывает на начало сохранённого регистра ebp
    // 
    // далее граница стека (видимо, из дебажных соображений?) сдвигается 
    // на 0x48 байт, в эту область пихаются локальные переменные, остаток
    // заполняется 0xCCCC, а после этой области сохраняются регистры
    // 
    // итого, стек выглядит так:
    //
    // параметры
    // адрес возврата (слово)
    // регистр ebp (слово)
    // локальные перемнные
    // пустота с 0xCC
    // сохранённые регистры
    // -----------------
    // свободно
    //
    // В нормальном оторажении (адреса снизу вверх):
    //
    //         свободно 
    //         -----------------------
    // esp ->  сохранённые регистры
    //         пустота с 0xCC
    //         локальные пременные
    // ebp ->  регистр ebp (слово)
    //         адрес возврата (слово)
    //         параметры
    // 
    // значит, интересная (копируемая) область начинается с esp и идет до ebp + 8

    void* stack_copy = NULL;
    int   stack_len = 0;

    //
    // Вычисление длинны копируемой области
    //

    __asm
    {
        mov    stack_len, ebp
        sub    stack_len, esp
    }

    stack_len += 2 * sizeof(int); // + ebp + адрес возврата

    // Выделяем память
    stack_copy = (char*)malloc(stack_len);

    __asm
    {
        // параметры movsb:
        // es::esi - строка источник
        // es::edi - строка приёмник
        // ecx - длина
        //

        // Копируем стек
        mov    ecx, stack_len
        mov esi, esp
        mov    edi, stack_copy

        push    ss
        pop        es

        rep    movsb

        // длину области памяти и её адрес нужно хранить в регистрах,
        // иначе f2 их испортит. Только не в eax, он испльзуется для 
        // возвращения результата. ecx и edx тоже портятся.. Вообще,
        // во всех (пяти) просмотренных прологах функций почему-то  
        // сохраняются только ebp, ebx, esi и edi. Это что ли 
        // соглашение такое? Никому нельзя верить..

        mov    ebx, stack_len
        mov esi, stack_copy

        // Очищаем стек
        add esp, ebx

        // вызываем scanf. Поскольку она тоже __cdecl, парамтры
        // для неё передаются гарантированно в том же порядке
        // и она не очищает стек.
        call scanf

        // Возвращаем стек на место
        sub esp, ebx

        // Копируем всё обратно
        // mov ecx, stack_len  - сделано заранее
        // mov esi, stack_copy - сделано заранее
        mov    ecx, ebx
        mov    edi, esp

        rep    movsb
    }
    
    free(stack_copy);

    return 0;
}

void main()
{
    int a, b;

    printf("enter a and b\n"); 

    f1("%d%d", &a, &b);

    printf("a = %d b = %d\n", a, b); 
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.