Вызвать библиотечную функцию (fscanf) со "своим" стеком?
От: Strudel  
Дата: 09.12.03 13:35
Оценка:
Можно ли под windows используя VC подставить библиотечной функции стек(параметры) собранный "ручками"

int f(const char*,....); // такой вот прототип нашей функции, вызывающей scanf


//
int f("несущественно", a,b,c)
{
// делаем что-то умное
// вызываем scanf так, что он имеет параметры как  f() - "несущественно", a,b,c
}
Re: Вызвать библиотечную функцию (fscanf) со "своим" стеком?
От: SergH Россия  
Дата: 09.12.03 13:39
Оценка:
Здравствуйте, Strudel, Вы писали:

S>Можно ли под windows используя VC подставить библиотечной функции стек(параметры) собранный "ручками"


S>
S>int f(const char*,....); // такой вот прототип нашей функции, вызывающей scanf


S>//
S>int f("несущественно", a,b,c)
S>{
S>// делаем что-то умное
S>// вызываем scanf так, что он имеет параметры как  f() - "несущественно", a,b,c
S>}
S>


А ты представь, как это сделать на асме. Я вот представил, и имхо на C++ такого лучше не делать..
Делай что должно, и будь что будет
Re[2]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: Strudel  
Дата: 09.12.03 14:25
Оценка:
Хорошо, а позволят ли "c++ stream" сделать, красиво, вот такой ввод:


fscanf(in,"%[^=]=%[^\n]\n",par,val);
Re[3]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: SergH Россия  
Дата: 09.12.03 14:35
Оценка:
Здравствуйте, Strudel, Вы писали:

S>Хорошо, а позволят ли "c++ stream" сделать, красиво, вот такой ввод:

S>fscanf(in,"%[^=]=%[^\n]\n",par,val);

А не шёл б ты в форум C++ со своими вопросами ?
Ато про стримы я почти ничего не знаю..
Делай что должно, и будь что будет
Re: Вызвать библиотечную функцию (fscanf) со "своим" стеком?
От: Sergey Россия  
Дата: 09.12.03 14:54
Оценка:
Hello, Strudel!
You wrote on Tue, 09 Dec 2003 13:35:16 GMT:

S> Можно ли под windows используя VC подставить библиотечной функции

S> стек(параметры) собранный "ручками"

S>
 S> int f(const char*,....); // такой вот прототип нашей функции, вызывающей
 S> scanf

 S> //
 S> int f("несущественно", a,b,c)
 S> {
 S> // делаем что-то умное
 S> // вызываем scanf так, что он имеет параметры как  f() -
 S> "несущественно", a,b,c }
 S>


Можно, только windows тут совершенно не при чем. Смотреть va_arg, va_start, va_end.

Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: SergH Россия  
Дата: 09.12.03 15:03
Оценка:
Здравствуйте, Sergey, Вы писали:

S>Можно, только windows тут совершенно не при чем. Смотреть va_arg, va_start, va_end.


А не мог бы ты алгоритм подсказать? А то я чего-то пока не соображу..

Кстати, для printf есть vprintf, но тут-то scanf..
Делай что должно, и будь что будет
Re[2]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: Strudel  
Дата: 09.12.03 15:20
Оценка:
Здравствуйте, Sergey, Вы писали:

S>Можно, только windows тут совершенно не при чем. Смотреть va_arg, va_start, va_end.


S>Best regards,

S> Sergey.


По поводу windows и va_arg, va_start, va_end — в MSDN как раз unix особенности упомянуты. А еще есть разные MSVC- специфические(?) модификаторы вызова (так что-ли?).
Но главное не это? Для чего нам нужны va_arg, va_start, va_end и так понятно — можно извлечь параметры f().
Главное — что дальше? И нужно ли их извлекать? Или можно просто подставить слегка модифицированный стек, а дальше вызов
scanf — ручками.
Re[3]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: Sergey Россия  
Дата: 09.12.03 15:21
Оценка:
Hello, SergH!
You wrote on Tue, 09 Dec 2003 15:03:43 GMT:

S>> Можно, только windows тут совершенно не при чем. Смотреть va_arg,

S>> va_start, va_end.

S> А не мог бы ты алгоритм подсказать? А то я чего-то пока не соображу..


Выделить с помощью alloca блок нужного размера (форматную строку, само собой, придется разбирать самостоятельно, т.к. способа определить размер аргументов нет), скопировать туда аргументы по одному (тут упомянутые макросы пригодятся), вызвать scanf, скопировать аргументы назад.

S> Кстати, для printf есть vprintf, но тут-то scanf..


Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[4]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: Sergey Россия  
Дата: 09.12.03 15:27
Оценка:
Hello, Sergey!
You wrote on Tue, 09 Dec 2003 15:21:35 GMT:

Да блин, это я прогнал. Хрен так вызовешь. Надо ассемблерную вставку делать.

Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[3]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: Sergey Россия  
Дата: 09.12.03 15:33
Оценка:
Hello, Strudel!
You wrote on Tue, 09 Dec 2003 15:20:39 GMT:

S> По поводу windows и va_arg, va_start, va_end — в MSDN как раз unix

S> особенности упомянуты.

Один черт, винда тут не при чем.

S> А еще есть разные MSVC- специфические(?) модификаторы вызова (так

S> что-ли?).

AFAIK, нет таких для функций с переменным числом параметров.

S> Но главное не это? Для чего нам нужны va_arg, va_start, va_end

S> и так понятно — можно извлечь параметры f(). Главное — что дальше?

S> И

S> нужно ли их извлекать?

S> Или можно просто подставить слегка

S> модифицированный стек, а дальше вызов scanf — ручками.

примерно так:

void f(const char*fmtstr, ...)
long *tmpsp;
const char** firstarg = &fmtstr;
__asm
{
mov tmpsp, esp
mov esp, firstarg
call scanf
mov esp, tmpesp
}

Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[4]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: SergH Россия  
Дата: 09.12.03 15:38
Оценка:
Здравствуйте, Sergey, Вы писали:

S>форматную строку, само собой, придется разбирать самостоятельно, т.к. способа определить размер аргументов нет


Э, с разбором то можно, наверное. Но разбирать-то нехочется. Я что-то такое придумал.

очистить стек от временных переменных
вытащить оттуда адрес возврата и сохранить в регистрах
вызвать fscanf
вернуть в стек адрес возврата
закончиться

S>void f(const char*fmtstr, ...)

S>{
S>long *tmpsp;
S>const char** firstarg = &fmtstr;
S>__asm
S>{
S>mov tmpsp, esp
S>mov esp, firstarg
S>call scanf
S>mov esp, tmpesp
S>}

Не, так низя. она же тебе адрес возврата изгадит нафиг.
Делай что должно, и будь что будет
Re[5]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: Sergey Россия  
Дата: 09.12.03 15:57
Оценка:
Hello, SergH!
You wrote on Tue, 09 Dec 2003 15:38:29 GMT:

S> Э, с разбором то можно, наверное. Но разбирать-то нехочется. Я что-то

S> такое придумал.

S> очистить стек от временных переменных


Зачем? Но если уж надо, у MSVC есть __declspec( naked ).

S> вытащить оттуда адрес возврата и сохранить в регистрах


Адрес первого аргумента известны, адрес возврата под ним лежит. Влияние алайнмента правда проверить придется.

S> вызвать fscanf

S> вернуть в стек адрес возврата
S> закончиться

S>> void f(const char*fmtstr, ...)

S>> {
S>> long *tmpsp;
S>> const char** firstarg = &fmtstr;
S>> __asm
S>> {
S>> mov tmpsp, esp
S>> mov esp, firstarg
S>> call scanf
S>> mov esp, tmpesp
S>> }

S> Не, так низя. она же тебе адрес возврата изгадит нафиг.


Да, еще пару строк дописать придется — адрес возврата сохранять.

void f(const char*fmtstr, ...)
long *tmpsp;
const char** firstarg = &fmtstr;
const long** retadrptr = (long**) firstarg - sizeof(long**);
const long* retadr = *retadrptr;
__asm
{
   mov tmpsp, esp
   mov esp, firstarg
   call scanf
   mov esp, tmpesp
}
*retadrptr = retadr;

Возможно, придется пошаманить и запихнуть firstarg и retadr в структуру, чтоб выравнивание учлось — точно не знаю.

Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[6]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: SergH Россия  
Дата: 09.12.03 16:07
Оценка:
Здравствуйте, Sergey, Вы писали:

S>> очистить стек от временных переменных


S>Зачем? Но если уж надо, у MSVC есть __declspec( naked ).


S>> вытащить оттуда адрес возврата и сохранить в регистрах


S>Адрес первого аргумента известны, адрес возврата под ним лежит. Влияние алайнмента правда проверить придется.


Это я знаю.

S>Да, еще пару строк дописать придется — адрес возврата сохранять.


S>
S>void f(const char*fmtstr, ...)
S>long *tmpsp;
S>const char** firstarg = &fmtstr;
S>const long** retadrptr = (long**) firstarg - sizeof(long**);
S>const long* retadr = *retadrptr;
S>__asm
S>{
S>   mov tmpsp, esp
S>   mov esp, firstarg
S>   call scanf
S>   mov esp, tmpesp
S>}
S>*retadrptr = retadr;
S>

S>Возможно, придется пошаманить и запихнуть firstarg и retadr в структуру, чтоб выравнивание учлось — точно не знаю.

Хм, очевидно у нас разное представление о стеке. Имхо там так (растет сверху вниз, как положено):

В самом начале функции:
....
параметры функции
адрес возврата
-------

свободно


Чуть погодя:
....
параметры функции
адрес возврата
-------
сохранённые регистры
локальные переменные
--------

свободно


Так вот, хочется мне выкинуть из стека всё, кроме "параметры функции" и вызвать fscanf. К сожалению, при этом приходится где-то в глобальной памяти (другом стеке?) хранить свои сохранённые регистры и свой адрес возврата. Именно для того чтобы его сохранить я и хочу его вытащить.

А то, что ты предлагаешь это просто установить SP, нигде ничего не сохранив. Точнее, сохранив в локальной переменной, которая находится в этом же стекеи которая, конечно, испортится при вызове fscanf. Заодно испортятся и сохраннные там регистры.
Делай что должно, и будь что будет
Re[7]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: Sergey Россия  
Дата: 09.12.03 16:53
Оценка:
Hello, SergH!
You wrote on Tue, 09 Dec 2003 16:07:33 GMT:

S> Хм, очевидно у нас разное представление о стеке. Имхо там так (растет

S> сверху вниз, как положено):

S> В самом начале функции:

S>
 S> ....
 S> параметры функции
 S> адрес возврата
 S> -------

 S> свободно
 S>


S> Чуть погодя:

S>
 S> ....
 S> параметры функции
 S> адрес возврата
 S> -------
 S> сохранённые регистры
 S> локальные переменные
 S> --------

 S> свободно
 S>


Ну да, так и есть.

S> Так вот, хочется мне выкинуть из стека всё, кроме "параметры функции" и

S> вызвать fscanf.

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

S> К сожалению, при этом приходится где-то в глобальной памяти (другом

S> стеке?) хранить свои сохранённые регистры и свой адрес возврата. Именно
S> для того чтобы его сохранить я и хочу его вытащить.

Чтоб его сохранить, достаточно знать его адрес. Локальные переменные для этого вытаскивать не надо.

S> А то, что ты предлагаешь это просто установить SP, нигде ничего не

S> сохранив. Точнее, сохранив в локальной переменной, которая находится в
S> этом же стекеи которая, конечно, испортится при вызове fscanf.

Да, запортится. Я просто прозевал.

Best regards,
Sergey.
Posted via RSDN NNTP Server 1.8 beta
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
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); 
}
Re[9]: Вызвать библиотечную функцию (fscanf) со "своим" стек
От: SergH Россия  
Дата: 10.12.03 23:59
Оценка:
Здравствуйте, TaniaDen, Вы писали:

TD>Вобщем, так работает..




TD> // длину области памяти и её адрес нужно хранить в регистрах,

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

Судя по этому вот

When using __asm to write assembly language in C/C++ functions, you don't need to preserve the EAX, EBX, ECX, EDX, ESI, or EDI registers. For example, in the POWER2.C example in Writing Functions with Inline Assembly, the power2 function doesn't preserve the value in the EAX register. However, using these registers will affect code quality because the register allocator cannot use them to store values across __asm blocks. In addition, by using EBX, ESI or EDI in inline assembly code, you force the compiler to save and restore those registers in the function prologue and epilogue.


http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core_Using_and_Preserving_Registers_in_Inline_Assembly.asp

да, похоже соглашение для VC. Ещё я нашёл одно упоминание, что Microsoft C сохраняет bp, di и si, но там имелся ввиду DOS..
Делай что должно, и будь что будет
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.